Merge "Add selection handle dragging tests."
diff --git a/Android.mk b/Android.mk
index 71bba0f..7ca04a5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -281,6 +281,7 @@
core/java/com/android/internal/app/IAppOpsService.aidl \
core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
+ core/java/com/android/internal/app/IEphemeralResolver.aidl \
core/java/com/android/internal/app/IProcessStats.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
@@ -343,8 +344,6 @@
media/java/android/media/IMediaRouterService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
- media/java/android/media/IRemoteControlClient.aidl \
- media/java/android/media/IRemoteControlDisplay.aidl \
media/java/android/media/IRemoteDisplayCallback.aidl \
media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeController.aidl \
@@ -417,6 +416,8 @@
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
packages/services/Proxy/com/android/net/IProxyCallback.aidl \
packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
+ core/java/android/service/quicksettings/IQSService.aidl \
+ core/java/android/service/quicksettings/IQSTileService.aidl \
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
@@ -427,7 +428,7 @@
$(framework_res_source_path)/com/android/internal/R.java
LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext
+LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp core-junit bouncycastle ext
LOCAL_MODULE := framework
@@ -625,6 +626,7 @@
frameworks/base/core/java/android/bluetooth/le/ScanResult.aidl \
frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \
frameworks/base/core/java/android/database/CursorWindow.aidl \
+ frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
@@ -715,6 +717,7 @@
$(framework_res_source_path)/com/android/internal/R.java
framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
+ core-oj \
core-libart \
conscrypt \
bouncycastle \
@@ -738,6 +741,7 @@
# not be referenced in the documentation.
framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-knowntags ./frameworks/base/docs/knowntags.txt \
+ -knowntags ./libcore/known_oj_tags.txt \
-hidePackage com.android.org.conscrypt \
-since $(SRC_API_DIR)/1.xml 1 \
-since $(SRC_API_DIR)/2.xml 2 \
@@ -1100,7 +1104,7 @@
LOCAL_SRC_FILES := $(ext_src_files)
LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-libart
+LOCAL_JAVA_LIBRARIES := core-oj core-libart
LOCAL_STATIC_JAVA_LIBRARIES := libphonenumber-platform
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := ext
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6e44d77..40908f1 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -236,6 +236,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DocumentsUI_intermediates)
+$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlClient.*)
+$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlDisplay.*)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index c161242..0774a9a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30,6 +30,7 @@
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
+ field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
@@ -332,6 +333,7 @@
field public static final int calendarTextColor = 16843931; // 0x101049b
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
+ field public static final int canControlMagnification = 16844040; // 0x1010508
field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -499,6 +501,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int encryptionAware = 16844038; // 0x1010506
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
@@ -559,6 +562,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceDeviceEncrypted = 16844037; // 0x1010505
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -931,6 +935,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1708,11 +1713,13 @@
field public static final int icon = 16908294; // 0x1020006
field public static final int icon1 = 16908295; // 0x1020007
field public static final int icon2 = 16908296; // 0x1020008
+ field public static final int icon_frame = 16908350; // 0x102003e
field public static final int input = 16908297; // 0x1020009
field public static final int inputArea = 16908318; // 0x102001e
field public static final int inputExtractEditText = 16908325; // 0x1020025
field public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
+ field public static final int list_container = 16908351; // 0x102003f
field public static final int mask = 16908334; // 0x102002e
field public static final int message = 16908299; // 0x102000b
field public static final int navigationBarBackground = 16908336; // 0x1020030
@@ -1732,6 +1739,7 @@
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
+ field public static final int switch_widget = 16908352; // 0x1020040
field public static final int tabcontent = 16908305; // 0x1020011
field public static final int tabhost = 16908306; // 0x1020012
field public static final int tabs = 16908307; // 0x1020013
@@ -2613,6 +2621,7 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+ method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
@@ -2650,6 +2659,23 @@
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
+ public static final class AccessibilityService.MagnificationController {
+ method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
+ method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
+ method public float getCenterX();
+ method public float getCenterY();
+ method public android.graphics.Region getMagnifiedRegion();
+ method public float getScale();
+ method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
+ method public boolean reset(boolean);
+ method public boolean setCenter(float, float, boolean);
+ method public boolean setScale(float, boolean);
+ }
+
+ public static abstract interface AccessibilityService.MagnificationController.OnMagnificationChangedListener {
+ method public abstract void onMagnificationChanged(android.accessibilityservice.AccessibilityService.MagnificationController, android.graphics.Region, float, float, float);
+ }
+
public class AccessibilityServiceInfo implements android.os.Parcelable {
ctor public AccessibilityServiceInfo();
method public static java.lang.String capabilityToString(int);
@@ -2664,6 +2690,7 @@
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2706,6 +2733,8 @@
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2770,6 +2799,8 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2786,6 +2817,8 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
+ field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+ field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3469,6 +3502,7 @@
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
+ method public void overlayWithDecorCaption(boolean);
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
@@ -3747,6 +3781,8 @@
}
public class ActivityOptions {
+ method public android.graphics.Rect getLaunchBounds();
+ method public boolean hasLaunchBounds();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3756,6 +3792,7 @@
method public static android.app.ActivityOptions makeTaskLaunchBehind();
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public void requestUsageTimeReport(android.app.PendingIntent);
+ method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
@@ -4769,7 +4806,7 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
- method public android.app.Notification.Topic[] getTopics();
+ method public android.app.Notification.Topic getTopic();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -4832,6 +4869,7 @@
field public static final int PRIORITY_MAX = 2; // 0x2
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final deprecated int STREAM_DEFAULT = -1; // 0xffffffff
+ field public static final java.lang.String TOPIC_DEFAULT = "system_default_topic";
field public static final int VISIBILITY_PRIVATE = 0; // 0x0
field public static final int VISIBILITY_PUBLIC = 1; // 0x1
field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
@@ -4934,7 +4972,6 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
- method public android.app.Notification.Builder addTopic(android.app.Notification.Topic);
method public android.app.Notification build();
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
@@ -4983,6 +5020,7 @@
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+ method public android.app.Notification.Builder setTopic(android.app.Notification.Topic);
method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setVisibility(int);
@@ -5140,10 +5178,12 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public NotificationManager.Policy(int, int, int);
+ ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
method public static java.lang.String prioritySendersToString(int);
+ method public static java.lang.String suppressedEffectsToString(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
@@ -5154,9 +5194,13 @@
field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0
field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1
field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2
+ field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff
+ field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
+ field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
field public final int priorityCallSenders;
field public final int priorityCategories;
field public final int priorityMessageSenders;
+ field public final int suppressedVisualEffects;
}
public final class PendingIntent implements android.os.Parcelable {
@@ -5705,6 +5749,7 @@
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5731,6 +5776,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
+ method public java.lang.String getWifiMacAddress();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5757,6 +5803,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -5822,6 +5869,8 @@
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+ field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
+ field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
@@ -9912,6 +9961,7 @@
public static class Resources.NotFoundException extends java.lang.RuntimeException {
ctor public Resources.NotFoundException();
ctor public Resources.NotFoundException(java.lang.String);
+ ctor public Resources.NotFoundException(java.lang.String, java.lang.Exception);
}
public final class Resources.Theme {
@@ -13417,6 +13467,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_TIMESTAMP_SOURCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_WHITE_LEVEL;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_MAX_ANALOG_SENSITIVITY;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect[]> SENSOR_OPTICAL_BLACK_REGIONS;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_ORIENTATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_REFERENCE_ILLUMINANT1;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> SENSOR_REFERENCE_ILLUMINANT2;
@@ -13829,6 +13880,8 @@
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
+ field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_DYNAMIC_BLACK_LEVEL;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_DYNAMIC_WHITE_LEVEL;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_FRAME_DURATION;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> SENSOR_GREEN_SPLIT;
@@ -17768,6 +17821,7 @@
package android.media.tv {
public final class TvContentRating {
+ method public final boolean contains(android.media.tv.TvContentRating);
method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
method public java.lang.String flattenToString();
method public java.lang.String getDomain();
@@ -25750,6 +25804,7 @@
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String CONTENT_VCARD_TYPE = "text/x-vcard";
field public static final android.net.Uri CONTENT_VCARD_URI;
+ field public static final android.net.Uri CORP_CONTENT_FILTER_URI;
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
@@ -25870,12 +25925,15 @@
}
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
+ method public static boolean isEnterpriseDirectoryId(long);
+ method public static boolean isRemoteDirectory(long);
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
field public static final java.lang.String ACCOUNT_TYPE = "accountType";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
field public static final android.net.Uri CONTENT_URI;
+ field public static final android.net.Uri CORP_CONTENT_URI;
field public static final long DEFAULT = 0L; // 0x0L
field public static final java.lang.String DIRECTORY_AUTHORITY = "authority";
field public static final java.lang.String DISPLAY_NAME = "displayName";
@@ -26235,6 +26293,7 @@
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
@@ -28964,12 +29023,15 @@
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
+ field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
+ field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
}
public static class NotificationListenerService.Ranking {
ctor public NotificationListenerService.Ranking();
method public java.lang.String getKey();
method public int getRank();
+ method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
}
@@ -29004,6 +29066,35 @@
}
+package android.service.quicksettings {
+
+ public final class Tile implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.CharSequence getContentDescription();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.lang.CharSequence getLabel();
+ method public void setContentDescription(java.lang.CharSequence);
+ method public void setIcon(android.graphics.drawable.Icon);
+ method public void setLabel(java.lang.CharSequence);
+ method public void updateTile();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+ }
+
+ public class TileService extends android.app.Service {
+ ctor public TileService();
+ method public final android.service.quicksettings.Tile getQsTile();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public void onClick();
+ method public void onStartListening();
+ method public void onStopListening();
+ method public void onTileAdded();
+ method public void onTileRemoved();
+ field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+ }
+
+}
+
package android.service.restrictions {
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
@@ -34368,6 +34459,7 @@
ctor public LocaleList(java.util.Locale[]);
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public java.util.Locale getBestMatch(java.lang.String[]);
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getPrimary();
@@ -36199,6 +36291,7 @@
method public boolean canResolveTextDirection();
method public boolean canScrollHorizontally(int);
method public boolean canScrollVertically(int);
+ method public final void cancelDragAndDrop();
method public void cancelLongPress();
method public final void cancelPendingInputEvents();
method public boolean checkInputConnectionProxy(android.view.View);
@@ -36216,6 +36309,7 @@
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
+ method public final boolean didLayoutParamsChange();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
method public void dispatchDisplayHint(int);
@@ -36441,6 +36535,7 @@
method public boolean isOpaque();
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
+ method public final boolean isPartialLayoutRequested();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -36677,11 +36772,13 @@
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
- method public final boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final deprecated boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable);
+ method public final void updateDragShadow(android.view.View.DragShadowBuilder);
method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
method public boolean willNotCacheDrawing();
method public boolean willNotDraw();
@@ -37050,6 +37147,7 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
+ method public int findDependentLayoutAxes(android.view.View, int);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -37116,6 +37214,8 @@
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
+ method public void requestLayoutForChild(android.view.View);
+ method public void requestPartialLayoutForChild(android.view.View);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public void scheduleLayoutAnimation();
@@ -37230,6 +37330,7 @@
method public abstract void childHasTransientStateChanged(android.view.View, boolean);
method public abstract void clearChildFocus(android.view.View);
method public abstract void createContextMenu(android.view.ContextMenu);
+ method public abstract int findDependentLayoutAxes(android.view.View, int);
method public abstract android.view.View focusSearch(android.view.View, int);
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -37259,12 +37360,16 @@
method public abstract void requestDisallowInterceptTouchEvent(boolean);
method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
+ method public abstract void requestLayoutForChild(android.view.View);
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
+ field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
+ field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
+ field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
}
public class ViewPropertyAnimator {
@@ -42656,13 +42761,18 @@
package java.awt.font {
public final class NumericShaper implements java.io.Serializable {
- method public static java.awt.font.NumericShaper getContextualShaper(int, int);
method public static java.awt.font.NumericShaper getContextualShaper(int);
+ method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>);
+ method public static java.awt.font.NumericShaper getContextualShaper(int, int);
+ method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>, java.awt.font.NumericShaper.Range);
+ method public java.util.Set<java.awt.font.NumericShaper.Range> getRangeSet();
method public int getRanges();
method public static java.awt.font.NumericShaper getShaper(int);
+ method public static java.awt.font.NumericShaper getShaper(java.awt.font.NumericShaper.Range);
method public boolean isContextual();
- method public void shape(char[], int, int, int);
method public void shape(char[], int, int);
+ method public void shape(char[], int, int, int);
+ method public void shape(char[], int, int, java.awt.font.NumericShaper.Range);
field public static final int ALL_RANGES = 524287; // 0x7ffff
field public static final int ARABIC = 2; // 0x2
field public static final int BENGALI = 16; // 0x10
@@ -42685,6 +42795,46 @@
field public static final int TIBETAN = 16384; // 0x4000
}
+ public static class NumericShaper.Range extends java.lang.Enum {
+ method public static java.awt.font.NumericShaper.Range valueOf(java.lang.String);
+ method public static final java.awt.font.NumericShaper.Range[] values();
+ enum_constant public static final java.awt.font.NumericShaper.Range ARABIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range BALINESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range BENGALI;
+ enum_constant public static final java.awt.font.NumericShaper.Range CHAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range DEVANAGARI;
+ enum_constant public static final java.awt.font.NumericShaper.Range EASTERN_ARABIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range ETHIOPIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range EUROPEAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range GUJARATI;
+ enum_constant public static final java.awt.font.NumericShaper.Range GURMUKHI;
+ enum_constant public static final java.awt.font.NumericShaper.Range JAVANESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range KANNADA;
+ enum_constant public static final java.awt.font.NumericShaper.Range KAYAH_LI;
+ enum_constant public static final java.awt.font.NumericShaper.Range KHMER;
+ enum_constant public static final java.awt.font.NumericShaper.Range LAO;
+ enum_constant public static final java.awt.font.NumericShaper.Range LEPCHA;
+ enum_constant public static final java.awt.font.NumericShaper.Range LIMBU;
+ enum_constant public static final java.awt.font.NumericShaper.Range MALAYALAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range MEETEI_MAYEK;
+ enum_constant public static final java.awt.font.NumericShaper.Range MONGOLIAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR;
+ enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR_SHAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range NEW_TAI_LUE;
+ enum_constant public static final java.awt.font.NumericShaper.Range NKO;
+ enum_constant public static final java.awt.font.NumericShaper.Range OL_CHIKI;
+ enum_constant public static final java.awt.font.NumericShaper.Range ORIYA;
+ enum_constant public static final java.awt.font.NumericShaper.Range SAURASHTRA;
+ enum_constant public static final java.awt.font.NumericShaper.Range SUNDANESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_HORA;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_THAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAMIL;
+ enum_constant public static final java.awt.font.NumericShaper.Range TELUGU;
+ enum_constant public static final java.awt.font.NumericShaper.Range THAI;
+ enum_constant public static final java.awt.font.NumericShaper.Range TIBETAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range VAI;
+ }
+
public final class TextAttribute extends java.text.AttributedCharacterIterator.Attribute {
ctor protected TextAttribute(java.lang.String);
field public static final java.awt.font.TextAttribute BACKGROUND;
@@ -42778,20 +42928,20 @@
public class PropertyChangeSupport implements java.io.Serializable {
ctor public PropertyChangeSupport(java.lang.Object);
- method public void addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void addPropertyChangeListener(java.beans.PropertyChangeListener);
+ method public void addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void fireIndexedPropertyChange(java.lang.String, int, java.lang.Object, java.lang.Object);
- method public void fireIndexedPropertyChange(java.lang.String, int, boolean, boolean);
method public void fireIndexedPropertyChange(java.lang.String, int, int, int);
+ method public void fireIndexedPropertyChange(java.lang.String, int, boolean, boolean);
method public void firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object);
- method public void firePropertyChange(java.lang.String, boolean, boolean);
method public void firePropertyChange(java.lang.String, int, int);
+ method public void firePropertyChange(java.lang.String, boolean, boolean);
method public void firePropertyChange(java.beans.PropertyChangeEvent);
- method public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String);
method public java.beans.PropertyChangeListener[] getPropertyChangeListeners();
+ method public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String);
method public boolean hasListeners(java.lang.String);
- method public void removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void removePropertyChangeListener(java.beans.PropertyChangeListener);
+ method public void removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
}
}
@@ -42816,8 +42966,8 @@
}
public class BufferedReader extends java.io.Reader {
- ctor public BufferedReader(java.io.Reader);
ctor public BufferedReader(java.io.Reader, int);
+ ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
@@ -42846,10 +42996,10 @@
ctor public ByteArrayOutputStream();
ctor public ByteArrayOutputStream(int);
method public synchronized void reset();
- method public int size();
+ method public synchronized int size();
method public synchronized byte[] toByteArray();
- method public deprecated java.lang.String toString(int);
- method public java.lang.String toString(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public synchronized java.lang.String toString(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public deprecated synchronized java.lang.String toString(int);
method public synchronized void write(int);
method public synchronized void writeTo(java.io.OutputStream) throws java.io.IOException;
field protected byte[] buf;
@@ -42891,13 +43041,15 @@
}
public final class Console implements java.io.Flushable {
+ method public static java.io.Console console();
method public void flush();
method public java.io.Console format(java.lang.String, java.lang.Object...);
+ method public static synchronized java.io.Console getConsole();
method public java.io.Console printf(java.lang.String, java.lang.Object...);
- method public java.lang.String readLine();
method public java.lang.String readLine(java.lang.String, java.lang.Object...);
- method public char[] readPassword();
+ method public java.lang.String readLine();
method public char[] readPassword(java.lang.String, java.lang.Object...);
+ method public char[] readPassword();
method public java.io.Reader reader();
method public java.io.PrintWriter writer();
}
@@ -42943,9 +43095,9 @@
}
public abstract interface DataOutput {
+ method public abstract void write(int) throws java.io.IOException;
method public abstract void write(byte[]) throws java.io.IOException;
method public abstract void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
method public abstract void writeBoolean(boolean) throws java.io.IOException;
method public abstract void writeByte(int) throws java.io.IOException;
method public abstract void writeBytes(java.lang.String) throws java.io.IOException;
@@ -42987,17 +43139,17 @@
}
public class File implements java.lang.Comparable java.io.Serializable {
- ctor public File(java.io.File, java.lang.String);
ctor public File(java.lang.String);
ctor public File(java.lang.String, java.lang.String);
+ ctor public File(java.io.File, java.lang.String);
ctor public File(java.net.URI);
method public boolean canExecute();
method public boolean canRead();
method public boolean canWrite();
method public int compareTo(java.io.File);
method public boolean createNewFile() throws java.io.IOException;
- method public static java.io.File createTempFile(java.lang.String, java.lang.String) throws java.io.IOException;
method public static java.io.File createTempFile(java.lang.String, java.lang.String, java.io.File) throws java.io.IOException;
+ method public static java.io.File createTempFile(java.lang.String, java.lang.String) throws java.io.IOException;
method public boolean delete();
method public void deleteOnExit();
method public boolean exists();
@@ -43035,6 +43187,7 @@
method public boolean setReadable(boolean);
method public boolean setWritable(boolean, boolean);
method public boolean setWritable(boolean);
+ method public java.nio.file.Path toPath();
method public java.net.URI toURI();
method public deprecated java.net.URL toURL() throws java.net.MalformedURLException;
field public static final java.lang.String pathSeparator;
@@ -43057,9 +43210,9 @@
}
public class FileInputStream extends java.io.InputStream {
+ ctor public FileInputStream(java.lang.String) throws java.io.FileNotFoundException;
ctor public FileInputStream(java.io.File) throws java.io.FileNotFoundException;
ctor public FileInputStream(java.io.FileDescriptor);
- ctor public FileInputStream(java.lang.String) throws java.io.FileNotFoundException;
method public java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public int read() throws java.io.IOException;
@@ -43071,11 +43224,11 @@
}
public class FileOutputStream extends java.io.OutputStream {
+ ctor public FileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
+ ctor public FileOutputStream(java.lang.String, boolean) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.File) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.File, boolean) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.FileDescriptor);
- ctor public FileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
- ctor public FileOutputStream(java.lang.String, boolean) throws java.io.FileNotFoundException;
method public java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public void write(int) throws java.io.IOException;
@@ -43083,22 +43236,24 @@
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
public class FileReader extends java.io.InputStreamReader {
+ ctor public FileReader(java.lang.String) throws java.io.FileNotFoundException;
ctor public FileReader(java.io.File) throws java.io.FileNotFoundException;
ctor public FileReader(java.io.FileDescriptor);
- ctor public FileReader(java.lang.String) throws java.io.FileNotFoundException;
}
public class FileWriter extends java.io.OutputStreamWriter {
+ ctor public FileWriter(java.lang.String) throws java.io.IOException;
+ ctor public FileWriter(java.lang.String, boolean) throws java.io.IOException;
ctor public FileWriter(java.io.File) throws java.io.IOException;
ctor public FileWriter(java.io.File, boolean) throws java.io.IOException;
ctor public FileWriter(java.io.FileDescriptor);
- ctor public FileWriter(java.lang.String) throws java.io.IOException;
- ctor public FileWriter(java.lang.String, boolean) throws java.io.IOException;
}
public abstract interface FilenameFilter {
@@ -43151,7 +43306,7 @@
ctor public InputStream();
method public int available() throws java.io.IOException;
method public void close() throws java.io.IOException;
- method public void mark(int);
+ method public synchronized void mark(int);
method public boolean markSupported();
method public abstract int read() throws java.io.IOException;
method public int read(byte[]) throws java.io.IOException;
@@ -43163,8 +43318,8 @@
public class InputStreamReader extends java.io.Reader {
ctor public InputStreamReader(java.io.InputStream);
ctor public InputStreamReader(java.io.InputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public InputStreamReader(java.io.InputStream, java.nio.charset.CharsetDecoder);
ctor public InputStreamReader(java.io.InputStream, java.nio.charset.Charset);
+ ctor public InputStreamReader(java.io.InputStream, java.nio.charset.CharsetDecoder);
method public void close() throws java.io.IOException;
method public java.lang.String getEncoding();
method public int read(char[], int, int) throws java.io.IOException;
@@ -43173,6 +43328,7 @@
public class InterruptedIOException extends java.io.IOException {
ctor public InterruptedIOException();
ctor public InterruptedIOException(java.lang.String);
+ ctor public InterruptedIOException(java.lang.Throwable);
field public int bytesTransferred;
}
@@ -43200,13 +43356,13 @@
}
public class NotActiveException extends java.io.ObjectStreamException {
- ctor public NotActiveException();
ctor public NotActiveException(java.lang.String);
+ ctor public NotActiveException();
}
public class NotSerializableException extends java.io.ObjectStreamException {
- ctor public NotSerializableException();
ctor public NotSerializableException(java.lang.String);
+ ctor public NotSerializableException();
}
public abstract interface ObjectInput implements java.lang.AutoCloseable java.io.DataInput {
@@ -43220,32 +43376,32 @@
}
public class ObjectInputStream extends java.io.InputStream implements java.io.ObjectInput java.io.ObjectStreamConstants {
- ctor protected ObjectInputStream() throws java.io.IOException;
- ctor public ObjectInputStream(java.io.InputStream) throws java.io.IOException, java.io.StreamCorruptedException;
- method public void defaultReadObject() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.NotActiveException;
- method protected boolean enableResolveObject(boolean);
+ ctor public ObjectInputStream(java.io.InputStream) throws java.io.IOException;
+ ctor protected ObjectInputStream() throws java.io.IOException, java.lang.SecurityException;
+ method public void defaultReadObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+ method protected boolean enableResolveObject(boolean) throws java.lang.SecurityException;
method public int read() throws java.io.IOException;
method public boolean readBoolean() throws java.io.IOException;
method public byte readByte() throws java.io.IOException;
method public char readChar() throws java.io.IOException;
method protected java.io.ObjectStreamClass readClassDescriptor() throws java.lang.ClassNotFoundException, java.io.IOException;
method public double readDouble() throws java.io.IOException;
- method public java.io.ObjectInputStream.GetField readFields() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.NotActiveException;
+ method public java.io.ObjectInputStream.GetField readFields() throws java.lang.ClassNotFoundException, java.io.IOException;
method public float readFloat() throws java.io.IOException;
method public void readFully(byte[]) throws java.io.IOException;
method public void readFully(byte[], int, int) throws java.io.IOException;
method public int readInt() throws java.io.IOException;
method public deprecated java.lang.String readLine() throws java.io.IOException;
method public long readLong() throws java.io.IOException;
- method public final java.lang.Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.OptionalDataException;
- method protected java.lang.Object readObjectOverride() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.OptionalDataException;
+ method public final java.lang.Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+ method protected java.lang.Object readObjectOverride() throws java.lang.ClassNotFoundException, java.io.IOException;
method public short readShort() throws java.io.IOException;
method protected void readStreamHeader() throws java.io.IOException, java.io.StreamCorruptedException;
method public java.lang.String readUTF() throws java.io.IOException;
method public java.lang.Object readUnshared() throws java.lang.ClassNotFoundException, java.io.IOException;
method public int readUnsignedByte() throws java.io.IOException;
method public int readUnsignedShort() throws java.io.IOException;
- method public synchronized void registerValidation(java.io.ObjectInputValidation, int) throws java.io.InvalidObjectException, java.io.NotActiveException;
+ method public void registerValidation(java.io.ObjectInputValidation, int) throws java.io.InvalidObjectException, java.io.NotActiveException;
method protected java.lang.Class<?> resolveClass(java.io.ObjectStreamClass) throws java.lang.ClassNotFoundException, java.io.IOException;
method protected java.lang.Object resolveObject(java.lang.Object) throws java.io.IOException;
method protected java.lang.Class<?> resolveProxyClass(java.lang.String[]) throws java.lang.ClassNotFoundException, java.io.IOException;
@@ -43254,16 +43410,16 @@
public static abstract class ObjectInputStream.GetField {
ctor public ObjectInputStream.GetField();
- method public abstract boolean defaulted(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract boolean get(java.lang.String, boolean) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract char get(java.lang.String, char) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract byte get(java.lang.String, byte) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract short get(java.lang.String, short) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract int get(java.lang.String, int) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract long get(java.lang.String, long) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract float get(java.lang.String, float) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract double get(java.lang.String, double) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract java.lang.Object get(java.lang.String, java.lang.Object) throws java.io.IOException, java.lang.IllegalArgumentException;
+ method public abstract boolean defaulted(java.lang.String) throws java.io.IOException;
+ method public abstract boolean get(java.lang.String, boolean) throws java.io.IOException;
+ method public abstract byte get(java.lang.String, byte) throws java.io.IOException;
+ method public abstract char get(java.lang.String, char) throws java.io.IOException;
+ method public abstract short get(java.lang.String, short) throws java.io.IOException;
+ method public abstract int get(java.lang.String, int) throws java.io.IOException;
+ method public abstract long get(java.lang.String, long) throws java.io.IOException;
+ method public abstract float get(java.lang.String, float) throws java.io.IOException;
+ method public abstract double get(java.lang.String, double) throws java.io.IOException;
+ method public abstract java.lang.Object get(java.lang.String, java.lang.Object) throws java.io.IOException;
method public abstract java.io.ObjectStreamClass getObjectStreamClass();
}
@@ -43274,20 +43430,20 @@
public abstract interface ObjectOutput implements java.lang.AutoCloseable java.io.DataOutput {
method public abstract void close() throws java.io.IOException;
method public abstract void flush() throws java.io.IOException;
+ method public abstract void write(int) throws java.io.IOException;
method public abstract void write(byte[]) throws java.io.IOException;
method public abstract void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
method public abstract void writeObject(java.lang.Object) throws java.io.IOException;
}
public class ObjectOutputStream extends java.io.OutputStream implements java.io.ObjectOutput java.io.ObjectStreamConstants {
- ctor protected ObjectOutputStream() throws java.io.IOException;
ctor public ObjectOutputStream(java.io.OutputStream) throws java.io.IOException;
+ ctor protected ObjectOutputStream() throws java.io.IOException, java.lang.SecurityException;
method protected void annotateClass(java.lang.Class<?>) throws java.io.IOException;
method protected void annotateProxyClass(java.lang.Class<?>) throws java.io.IOException;
method public void defaultWriteObject() throws java.io.IOException;
method protected void drain() throws java.io.IOException;
- method protected boolean enableReplaceObject(boolean);
+ method protected boolean enableReplaceObject(boolean) throws java.lang.SecurityException;
method public java.io.ObjectOutputStream.PutField putFields() throws java.io.IOException;
method protected java.lang.Object replaceObject(java.lang.Object) throws java.io.IOException;
method public void reset() throws java.io.IOException;
@@ -43315,8 +43471,8 @@
public static abstract class ObjectOutputStream.PutField {
ctor public ObjectOutputStream.PutField();
method public abstract void put(java.lang.String, boolean);
- method public abstract void put(java.lang.String, char);
method public abstract void put(java.lang.String, byte);
+ method public abstract void put(java.lang.String, char);
method public abstract void put(java.lang.String, short);
method public abstract void put(java.lang.String, int);
method public abstract void put(java.lang.String, long);
@@ -43370,8 +43526,8 @@
}
public abstract class ObjectStreamException extends java.io.IOException {
- ctor protected ObjectStreamException();
ctor protected ObjectStreamException(java.lang.String);
+ ctor protected ObjectStreamException();
}
public class ObjectStreamField implements java.lang.Comparable {
@@ -43397,14 +43553,14 @@
ctor public OutputStream();
method public void close() throws java.io.IOException;
method public void flush() throws java.io.IOException;
+ method public abstract void write(int) throws java.io.IOException;
method public void write(byte[]) throws java.io.IOException;
method public void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
}
public class OutputStreamWriter extends java.io.Writer {
- ctor public OutputStreamWriter(java.io.OutputStream);
ctor public OutputStreamWriter(java.io.OutputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public OutputStreamWriter(java.io.OutputStream);
ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.Charset);
ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.CharsetEncoder);
method public void close() throws java.io.IOException;
@@ -43414,10 +43570,10 @@
}
public class PipedInputStream extends java.io.InputStream {
- ctor public PipedInputStream();
ctor public PipedInputStream(java.io.PipedOutputStream) throws java.io.IOException;
- ctor public PipedInputStream(int);
ctor public PipedInputStream(java.io.PipedOutputStream, int) throws java.io.IOException;
+ ctor public PipedInputStream();
+ ctor public PipedInputStream(int);
method public void connect(java.io.PipedOutputStream) throws java.io.IOException;
method public synchronized int read() throws java.io.IOException;
method protected synchronized void receive(int) throws java.io.IOException;
@@ -43428,28 +43584,28 @@
}
public class PipedOutputStream extends java.io.OutputStream {
- ctor public PipedOutputStream();
ctor public PipedOutputStream(java.io.PipedInputStream) throws java.io.IOException;
- method public void connect(java.io.PipedInputStream) throws java.io.IOException;
+ ctor public PipedOutputStream();
+ method public synchronized void connect(java.io.PipedInputStream) throws java.io.IOException;
method public void write(int) throws java.io.IOException;
}
public class PipedReader extends java.io.Reader {
- ctor public PipedReader();
ctor public PipedReader(java.io.PipedWriter) throws java.io.IOException;
- ctor public PipedReader(int);
ctor public PipedReader(java.io.PipedWriter, int) throws java.io.IOException;
- method public synchronized void close() throws java.io.IOException;
+ ctor public PipedReader();
+ ctor public PipedReader(int);
+ method public void close() throws java.io.IOException;
method public void connect(java.io.PipedWriter) throws java.io.IOException;
method public synchronized int read(char[], int, int) throws java.io.IOException;
}
public class PipedWriter extends java.io.Writer {
- ctor public PipedWriter();
ctor public PipedWriter(java.io.PipedReader) throws java.io.IOException;
+ ctor public PipedWriter();
method public void close() throws java.io.IOException;
- method public void connect(java.io.PipedReader) throws java.io.IOException;
- method public void flush() throws java.io.IOException;
+ method public synchronized void connect(java.io.PipedReader) throws java.io.IOException;
+ method public synchronized void flush() throws java.io.IOException;
method public void write(char[], int, int) throws java.io.IOException;
}
@@ -43457,111 +43613,111 @@
ctor public PrintStream(java.io.OutputStream);
ctor public PrintStream(java.io.OutputStream, boolean);
ctor public PrintStream(java.io.OutputStream, boolean, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public PrintStream(java.io.File) throws java.io.FileNotFoundException;
- ctor public PrintStream(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
ctor public PrintStream(java.lang.String) throws java.io.FileNotFoundException;
ctor public PrintStream(java.lang.String, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
- method public java.io.PrintStream append(char);
+ ctor public PrintStream(java.io.File) throws java.io.FileNotFoundException;
+ ctor public PrintStream(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
method public java.io.PrintStream append(java.lang.CharSequence);
method public java.io.PrintStream append(java.lang.CharSequence, int, int);
+ method public java.io.PrintStream append(char);
method public boolean checkError();
method protected void clearError();
method public java.io.PrintStream format(java.lang.String, java.lang.Object...);
method public java.io.PrintStream format(java.util.Locale, java.lang.String, java.lang.Object...);
- method public void print(char[]);
+ method public void print(boolean);
method public void print(char);
- method public void print(double);
- method public void print(float);
method public void print(int);
method public void print(long);
+ method public void print(float);
+ method public void print(double);
+ method public void print(char[]);
+ method public void print(java.lang.String);
method public void print(java.lang.Object);
- method public synchronized void print(java.lang.String);
- method public void print(boolean);
method public java.io.PrintStream printf(java.lang.String, java.lang.Object...);
method public java.io.PrintStream printf(java.util.Locale, java.lang.String, java.lang.Object...);
method public void println();
- method public void println(char[]);
+ method public void println(boolean);
method public void println(char);
- method public void println(double);
- method public void println(float);
method public void println(int);
method public void println(long);
+ method public void println(float);
+ method public void println(double);
+ method public void println(char[]);
+ method public void println(java.lang.String);
method public void println(java.lang.Object);
- method public synchronized void println(java.lang.String);
- method public void println(boolean);
method protected void setError();
}
public class PrintWriter extends java.io.Writer {
- ctor public PrintWriter(java.io.OutputStream);
- ctor public PrintWriter(java.io.OutputStream, boolean);
ctor public PrintWriter(java.io.Writer);
ctor public PrintWriter(java.io.Writer, boolean);
- ctor public PrintWriter(java.io.File) throws java.io.FileNotFoundException;
- ctor public PrintWriter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public PrintWriter(java.io.OutputStream);
+ ctor public PrintWriter(java.io.OutputStream, boolean);
ctor public PrintWriter(java.lang.String) throws java.io.FileNotFoundException;
ctor public PrintWriter(java.lang.String, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public PrintWriter(java.io.File) throws java.io.FileNotFoundException;
+ ctor public PrintWriter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
method public boolean checkError();
method protected void clearError();
method public void close();
method public void flush();
method public java.io.PrintWriter format(java.lang.String, java.lang.Object...);
method public java.io.PrintWriter format(java.util.Locale, java.lang.String, java.lang.Object...);
- method public void print(char[]);
+ method public void print(boolean);
method public void print(char);
- method public void print(double);
- method public void print(float);
method public void print(int);
method public void print(long);
- method public void print(java.lang.Object);
+ method public void print(float);
+ method public void print(double);
+ method public void print(char[]);
method public void print(java.lang.String);
- method public void print(boolean);
+ method public void print(java.lang.Object);
method public java.io.PrintWriter printf(java.lang.String, java.lang.Object...);
method public java.io.PrintWriter printf(java.util.Locale, java.lang.String, java.lang.Object...);
method public void println();
- method public void println(char[]);
+ method public void println(boolean);
method public void println(char);
- method public void println(double);
- method public void println(float);
method public void println(int);
method public void println(long);
- method public void println(java.lang.Object);
+ method public void println(float);
+ method public void println(double);
+ method public void println(char[]);
method public void println(java.lang.String);
- method public void println(boolean);
+ method public void println(java.lang.Object);
method protected void setError();
method public void write(char[], int, int);
field protected java.io.Writer out;
}
public class PushbackInputStream extends java.io.FilterInputStream {
- ctor public PushbackInputStream(java.io.InputStream);
ctor public PushbackInputStream(java.io.InputStream, int);
- method public void unread(byte[]) throws java.io.IOException;
- method public void unread(byte[], int, int) throws java.io.IOException;
+ ctor public PushbackInputStream(java.io.InputStream);
method public void unread(int) throws java.io.IOException;
+ method public void unread(byte[], int, int) throws java.io.IOException;
+ method public void unread(byte[]) throws java.io.IOException;
field protected byte[] buf;
field protected int pos;
}
public class PushbackReader extends java.io.FilterReader {
- ctor public PushbackReader(java.io.Reader);
ctor public PushbackReader(java.io.Reader, int);
- method public void unread(char[]) throws java.io.IOException;
- method public void unread(char[], int, int) throws java.io.IOException;
+ ctor public PushbackReader(java.io.Reader);
method public void unread(int) throws java.io.IOException;
+ method public void unread(char[], int, int) throws java.io.IOException;
+ method public void unread(char[]) throws java.io.IOException;
}
public class RandomAccessFile implements java.io.Closeable java.io.DataInput java.io.DataOutput {
- ctor public RandomAccessFile(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
ctor public RandomAccessFile(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ ctor public RandomAccessFile(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
method public void close() throws java.io.IOException;
- method public final synchronized java.nio.channels.FileChannel getChannel();
+ method public final java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public long getFilePointer() throws java.io.IOException;
method public long length() throws java.io.IOException;
method public int read() throws java.io.IOException;
- method public int read(byte[]) throws java.io.IOException;
method public int read(byte[], int, int) throws java.io.IOException;
+ method public int read(byte[]) throws java.io.IOException;
method public final boolean readBoolean() throws java.io.IOException;
method public final byte readByte() throws java.io.IOException;
method public final char readChar() throws java.io.IOException;
@@ -43579,9 +43735,9 @@
method public void seek(long) throws java.io.IOException;
method public void setLength(long) throws java.io.IOException;
method public int skipBytes(int) throws java.io.IOException;
+ method public void write(int) throws java.io.IOException;
method public void write(byte[]) throws java.io.IOException;
method public void write(byte[], int, int) throws java.io.IOException;
- method public void write(int) throws java.io.IOException;
method public final void writeBoolean(boolean) throws java.io.IOException;
method public final void writeByte(int) throws java.io.IOException;
method public final void writeBytes(java.lang.String) throws java.io.IOException;
@@ -43601,10 +43757,10 @@
method public abstract void close() throws java.io.IOException;
method public void mark(int) throws java.io.IOException;
method public boolean markSupported();
+ method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public int read() throws java.io.IOException;
method public int read(char[]) throws java.io.IOException;
method public abstract int read(char[], int, int) throws java.io.IOException;
- method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public boolean ready() throws java.io.IOException;
method public void reset() throws java.io.IOException;
method public long skip(long) throws java.io.IOException;
@@ -43612,8 +43768,8 @@
}
public class SequenceInputStream extends java.io.InputStream {
- ctor public SequenceInputStream(java.io.InputStream, java.io.InputStream);
ctor public SequenceInputStream(java.util.Enumeration<? extends java.io.InputStream>);
+ ctor public SequenceInputStream(java.io.InputStream, java.io.InputStream);
method public int read() throws java.io.IOException;
}
@@ -43626,8 +43782,8 @@
}
public class StreamCorruptedException extends java.io.ObjectStreamException {
- ctor public StreamCorruptedException();
ctor public StreamCorruptedException(java.lang.String);
+ ctor public StreamCorruptedException();
}
public class StreamTokenizer {
@@ -43702,14 +43858,14 @@
public abstract class Writer implements java.lang.Appendable java.io.Closeable java.io.Flushable {
ctor protected Writer();
ctor protected Writer(java.lang.Object);
- method public java.io.Writer append(char) throws java.io.IOException;
method public java.io.Writer append(java.lang.CharSequence) throws java.io.IOException;
method public java.io.Writer append(java.lang.CharSequence, int, int) throws java.io.IOException;
+ method public java.io.Writer append(char) throws java.io.IOException;
method public abstract void close() throws java.io.IOException;
method public abstract void flush() throws java.io.IOException;
+ method public void write(int) throws java.io.IOException;
method public void write(char[]) throws java.io.IOException;
method public abstract void write(char[], int, int) throws java.io.IOException;
- method public void write(int) throws java.io.IOException;
method public void write(java.lang.String) throws java.io.IOException;
method public void write(java.lang.String, int, int) throws java.io.IOException;
field protected java.lang.Object lock;
@@ -43724,32 +43880,63 @@
ctor public AbstractMethodError(java.lang.String);
}
- abstract class AbstractStringBuilder {
+ abstract class AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence {
+ method public java.lang.AbstractStringBuilder append(java.lang.Object);
+ method public java.lang.AbstractStringBuilder append(java.lang.String);
+ method public java.lang.AbstractStringBuilder append(java.lang.StringBuffer);
+ method public java.lang.AbstractStringBuilder append(java.lang.CharSequence);
+ method public java.lang.AbstractStringBuilder append(java.lang.CharSequence, int, int);
+ method public java.lang.AbstractStringBuilder append(char[]);
+ method public java.lang.AbstractStringBuilder append(char[], int, int);
+ method public java.lang.AbstractStringBuilder append(boolean);
+ method public java.lang.AbstractStringBuilder append(char);
+ method public java.lang.AbstractStringBuilder append(int);
+ method public java.lang.AbstractStringBuilder append(long);
+ method public java.lang.AbstractStringBuilder append(float);
+ method public java.lang.AbstractStringBuilder append(double);
+ method public java.lang.AbstractStringBuilder appendCodePoint(int);
method public int capacity();
method public char charAt(int);
method public int codePointAt(int);
method public int codePointBefore(int);
method public int codePointCount(int, int);
+ method public java.lang.AbstractStringBuilder delete(int, int);
+ method public java.lang.AbstractStringBuilder deleteCharAt(int);
method public void ensureCapacity(int);
method public void getChars(int, int, char[], int);
method public int indexOf(java.lang.String);
method public int indexOf(java.lang.String, int);
+ method public java.lang.AbstractStringBuilder insert(int, char[], int, int);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.Object);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.String);
+ method public java.lang.AbstractStringBuilder insert(int, char[]);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.CharSequence);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.CharSequence, int, int);
+ method public java.lang.AbstractStringBuilder insert(int, boolean);
+ method public java.lang.AbstractStringBuilder insert(int, char);
+ method public java.lang.AbstractStringBuilder insert(int, int);
+ method public java.lang.AbstractStringBuilder insert(int, long);
+ method public java.lang.AbstractStringBuilder insert(int, float);
+ method public java.lang.AbstractStringBuilder insert(int, double);
method public int lastIndexOf(java.lang.String);
method public int lastIndexOf(java.lang.String, int);
method public int length();
method public int offsetByCodePoints(int, int);
+ method public java.lang.AbstractStringBuilder replace(int, int, java.lang.String);
+ method public java.lang.AbstractStringBuilder reverse();
method public void setCharAt(int, char);
method public void setLength(int);
method public java.lang.CharSequence subSequence(int, int);
method public java.lang.String substring(int);
method public java.lang.String substring(int, int);
+ method public abstract java.lang.String toString();
method public void trimToSize();
}
public abstract interface Appendable {
- method public abstract java.lang.Appendable append(char) throws java.io.IOException;
method public abstract java.lang.Appendable append(java.lang.CharSequence) throws java.io.IOException;
method public abstract java.lang.Appendable append(java.lang.CharSequence, int, int) throws java.io.IOException;
+ method public abstract java.lang.Appendable append(char) throws java.io.IOException;
}
public class ArithmeticException extends java.lang.RuntimeException {
@@ -43770,7 +43957,6 @@
public class AssertionError extends java.lang.Error {
ctor public AssertionError();
- ctor public AssertionError(java.lang.String, java.lang.Throwable);
ctor public AssertionError(java.lang.Object);
ctor public AssertionError(boolean);
ctor public AssertionError(char);
@@ -43778,6 +43964,7 @@
ctor public AssertionError(long);
ctor public AssertionError(float);
ctor public AssertionError(double);
+ ctor public AssertionError(java.lang.String, java.lang.Throwable);
}
public abstract interface AutoCloseable {
@@ -43785,16 +43972,16 @@
}
public final class Boolean implements java.lang.Comparable java.io.Serializable {
- ctor public Boolean(java.lang.String);
ctor public Boolean(boolean);
+ ctor public Boolean(java.lang.String);
method public boolean booleanValue();
method public static int compare(boolean, boolean);
method public int compareTo(java.lang.Boolean);
method public static boolean getBoolean(java.lang.String);
method public static boolean parseBoolean(java.lang.String);
method public static java.lang.String toString(boolean);
- method public static java.lang.Boolean valueOf(java.lang.String);
method public static java.lang.Boolean valueOf(boolean);
+ method public static java.lang.Boolean valueOf(java.lang.String);
field public static final java.lang.Boolean FALSE;
field public static final java.lang.Boolean TRUE;
field public static final java.lang.Class<java.lang.Boolean> TYPE;
@@ -43810,12 +43997,13 @@
method public float floatValue();
method public int intValue();
method public long longValue();
- method public static byte parseByte(java.lang.String) throws java.lang.NumberFormatException;
method public static byte parseByte(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static byte parseByte(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toHexString(byte, boolean);
method public static java.lang.String toString(byte);
- method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
- method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Byte valueOf(byte);
+ method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
field public static final byte MAX_VALUE = 127; // 0x7f
field public static final byte MIN_VALUE = -128; // 0xffffff80
field public static final int SIZE = 8; // 0x8
@@ -43988,7 +44176,7 @@
}
public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
- method public static java.lang.Character.UnicodeBlock forName(java.lang.String);
+ method public static final java.lang.Character.UnicodeBlock forName(java.lang.String);
method public static java.lang.Character.UnicodeBlock of(char);
method public static java.lang.Character.UnicodeBlock of(int);
field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
@@ -44203,6 +44391,109 @@
field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES;
}
+ public static final class Character.UnicodeScript extends java.lang.Enum {
+ method public static final java.lang.Character.UnicodeScript forName(java.lang.String);
+ method public static java.lang.Character.UnicodeScript of(int);
+ method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
+ method public static final java.lang.Character.UnicodeScript[] values();
+ enum_constant public static final java.lang.Character.UnicodeScript ARABIC;
+ enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript AVESTAN;
+ enum_constant public static final java.lang.Character.UnicodeScript BALINESE;
+ enum_constant public static final java.lang.Character.UnicodeScript BAMUM;
+ enum_constant public static final java.lang.Character.UnicodeScript BATAK;
+ enum_constant public static final java.lang.Character.UnicodeScript BENGALI;
+ enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO;
+ enum_constant public static final java.lang.Character.UnicodeScript BRAHMI;
+ enum_constant public static final java.lang.Character.UnicodeScript BRAILLE;
+ enum_constant public static final java.lang.Character.UnicodeScript BUGINESE;
+ enum_constant public static final java.lang.Character.UnicodeScript BUHID;
+ enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL;
+ enum_constant public static final java.lang.Character.UnicodeScript CARIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript CHAM;
+ enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE;
+ enum_constant public static final java.lang.Character.UnicodeScript COMMON;
+ enum_constant public static final java.lang.Character.UnicodeScript COPTIC;
+ enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM;
+ enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT;
+ enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC;
+ enum_constant public static final java.lang.Character.UnicodeScript DESERET;
+ enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI;
+ enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS;
+ enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GOTHIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GREEK;
+ enum_constant public static final java.lang.Character.UnicodeScript GUJARATI;
+ enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI;
+ enum_constant public static final java.lang.Character.UnicodeScript HAN;
+ enum_constant public static final java.lang.Character.UnicodeScript HANGUL;
+ enum_constant public static final java.lang.Character.UnicodeScript HANUNOO;
+ enum_constant public static final java.lang.Character.UnicodeScript HEBREW;
+ enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA;
+ enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC;
+ enum_constant public static final java.lang.Character.UnicodeScript INHERITED;
+ enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI;
+ enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript JAVANESE;
+ enum_constant public static final java.lang.Character.UnicodeScript KAITHI;
+ enum_constant public static final java.lang.Character.UnicodeScript KANNADA;
+ enum_constant public static final java.lang.Character.UnicodeScript KATAKANA;
+ enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI;
+ enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI;
+ enum_constant public static final java.lang.Character.UnicodeScript KHMER;
+ enum_constant public static final java.lang.Character.UnicodeScript LAO;
+ enum_constant public static final java.lang.Character.UnicodeScript LATIN;
+ enum_constant public static final java.lang.Character.UnicodeScript LEPCHA;
+ enum_constant public static final java.lang.Character.UnicodeScript LIMBU;
+ enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B;
+ enum_constant public static final java.lang.Character.UnicodeScript LISU;
+ enum_constant public static final java.lang.Character.UnicodeScript LYCIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript LYDIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM;
+ enum_constant public static final java.lang.Character.UnicodeScript MANDAIC;
+ enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK;
+ enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript MYANMAR;
+ enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE;
+ enum_constant public static final java.lang.Character.UnicodeScript NKO;
+ enum_constant public static final java.lang.Character.UnicodeScript OGHAM;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC;
+ enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI;
+ enum_constant public static final java.lang.Character.UnicodeScript ORIYA;
+ enum_constant public static final java.lang.Character.UnicodeScript OSMANYA;
+ enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA;
+ enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript REJANG;
+ enum_constant public static final java.lang.Character.UnicodeScript RUNIC;
+ enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN;
+ enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA;
+ enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript SINHALA;
+ enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE;
+ enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI;
+ enum_constant public static final java.lang.Character.UnicodeScript SYRIAC;
+ enum_constant public static final java.lang.Character.UnicodeScript TAGALOG;
+ enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_LE;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET;
+ enum_constant public static final java.lang.Character.UnicodeScript TAMIL;
+ enum_constant public static final java.lang.Character.UnicodeScript TELUGU;
+ enum_constant public static final java.lang.Character.UnicodeScript THAANA;
+ enum_constant public static final java.lang.Character.UnicodeScript THAI;
+ enum_constant public static final java.lang.Character.UnicodeScript TIBETAN;
+ enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH;
+ enum_constant public static final java.lang.Character.UnicodeScript UGARITIC;
+ enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN;
+ enum_constant public static final java.lang.Character.UnicodeScript VAI;
+ enum_constant public static final java.lang.Character.UnicodeScript YI;
+ }
+
public final class Class implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
method public java.lang.Class<? extends U> asSubclass(java.lang.Class<U>);
method public T cast(java.lang.Object);
@@ -44215,28 +44506,28 @@
method public java.lang.ClassLoader getClassLoader();
method public java.lang.Class<?>[] getClasses();
method public java.lang.Class<?> getComponentType();
- method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Constructor<?>[] getConstructors();
+ method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?>[] getDeclaredClasses();
- method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors();
+ method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
method public java.lang.reflect.Field getDeclaredField(java.lang.String) throws java.lang.NoSuchFieldException;
method public java.lang.reflect.Field[] getDeclaredFields();
- method public java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Method[] getDeclaredMethods();
+ method public java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException;
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Class<?> getEnclosingClass();
method public java.lang.reflect.Constructor<?> getEnclosingConstructor();
method public java.lang.reflect.Method getEnclosingMethod();
method public T[] getEnumConstants();
method public java.lang.reflect.Field getField(java.lang.String) throws java.lang.NoSuchFieldException;
- method public java.lang.reflect.Field[] getFields();
+ method public java.lang.reflect.Field[] getFields() throws java.lang.SecurityException;
method public java.lang.reflect.Type[] getGenericInterfaces();
method public java.lang.reflect.Type getGenericSuperclass();
method public java.lang.Class<?>[] getInterfaces();
- method public java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Method[] getMethods();
+ method public java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Method[] getMethods() throws java.lang.SecurityException;
method public int getModifiers();
method public java.lang.String getName();
method public java.lang.Package getPackage();
@@ -44278,8 +44569,8 @@
}
public abstract class ClassLoader {
- ctor protected ClassLoader();
ctor protected ClassLoader(java.lang.ClassLoader);
+ ctor protected ClassLoader();
method public void clearAssertionStatus();
method protected final deprecated java.lang.Class<?> defineClass(byte[], int, int) throws java.lang.ClassFormatError;
method protected final java.lang.Class<?> defineClass(java.lang.String, byte[], int, int) throws java.lang.ClassFormatError;
@@ -44304,6 +44595,7 @@
method public static java.util.Enumeration<java.net.URL> getSystemResources(java.lang.String) throws java.io.IOException;
method public java.lang.Class<?> loadClass(java.lang.String) throws java.lang.ClassNotFoundException;
method protected java.lang.Class<?> loadClass(java.lang.String, boolean) throws java.lang.ClassNotFoundException;
+ method protected static boolean registerAsParallelCapable();
method protected final void resolveClass(java.lang.Class<?>);
method public void setClassAssertionStatus(java.lang.String, boolean);
method public void setDefaultAssertionStatus(boolean);
@@ -44351,10 +44643,10 @@
method public double doubleValue();
method public float floatValue();
method public int intValue();
- method public boolean isInfinite();
method public static boolean isInfinite(double);
- method public boolean isNaN();
+ method public boolean isInfinite();
method public static boolean isNaN(double);
+ method public boolean isNaN();
method public static double longBitsToDouble(long);
method public long longValue();
method public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException;
@@ -44398,6 +44690,7 @@
ctor public Error(java.lang.String);
ctor public Error(java.lang.String, java.lang.Throwable);
ctor public Error(java.lang.Throwable);
+ ctor protected Error(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public class Exception extends java.lang.Throwable {
@@ -44405,12 +44698,13 @@
ctor public Exception(java.lang.String);
ctor public Exception(java.lang.String, java.lang.Throwable);
ctor public Exception(java.lang.Throwable);
+ ctor protected Exception(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public class ExceptionInInitializerError extends java.lang.LinkageError {
ctor public ExceptionInInitializerError();
- ctor public ExceptionInInitializerError(java.lang.String);
ctor public ExceptionInInitializerError(java.lang.Throwable);
+ ctor public ExceptionInInitializerError(java.lang.String);
method public java.lang.Throwable getException();
}
@@ -44426,10 +44720,10 @@
method public float floatValue();
method public static float intBitsToFloat(int);
method public int intValue();
- method public boolean isInfinite();
method public static boolean isInfinite(float);
- method public boolean isNaN();
+ method public boolean isInfinite();
method public static boolean isNaN(float);
+ method public boolean isNaN();
method public long longValue();
method public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.String toHexString(float);
@@ -44525,8 +44819,8 @@
method public static int lowestOneBit(int);
method public static int numberOfLeadingZeros(int);
method public static int numberOfTrailingZeros(int);
- method public static int parseInt(java.lang.String) throws java.lang.NumberFormatException;
method public static int parseInt(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static int parseInt(java.lang.String) throws java.lang.NumberFormatException;
method public static int reverse(int);
method public static int reverseBytes(int);
method public static int rotateLeft(int, int);
@@ -44535,10 +44829,10 @@
method public static java.lang.String toBinaryString(int);
method public static java.lang.String toHexString(int);
method public static java.lang.String toOctalString(int);
- method public static java.lang.String toString(int);
method public static java.lang.String toString(int, int);
- method public static java.lang.Integer valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toString(int);
method public static java.lang.Integer valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Integer valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Integer valueOf(int);
field public static final int MAX_VALUE = 2147483647; // 0x7fffffff
field public static final int MIN_VALUE = -2147483648; // 0x80000000
@@ -44584,8 +44878,8 @@
method public static long lowestOneBit(long);
method public static int numberOfLeadingZeros(long);
method public static int numberOfTrailingZeros(long);
- method public static long parseLong(java.lang.String) throws java.lang.NumberFormatException;
method public static long parseLong(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static long parseLong(java.lang.String) throws java.lang.NumberFormatException;
method public static long reverse(long);
method public static long reverseBytes(long);
method public static long rotateLeft(long, int);
@@ -44594,10 +44888,10 @@
method public static java.lang.String toBinaryString(long);
method public static java.lang.String toHexString(long);
method public static java.lang.String toOctalString(long);
- method public static java.lang.String toString(long);
method public static java.lang.String toString(long, int);
- method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toString(long);
method public static java.lang.Long valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(long);
field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
@@ -44607,10 +44901,10 @@
public final class Math {
method public static double IEEEremainder(double, double);
- method public static double abs(double);
- method public static float abs(float);
method public static int abs(int);
method public static long abs(long);
+ method public static float abs(float);
+ method public static double abs(double);
method public static double acos(double);
method public static double asin(double);
method public static double atan(double);
@@ -44630,14 +44924,14 @@
method public static double log(double);
method public static double log10(double);
method public static double log1p(double);
- method public static double max(double, double);
- method public static float max(float, float);
method public static int max(int, int);
method public static long max(long, long);
- method public static double min(double, double);
- method public static float min(float, float);
+ method public static float max(float, float);
+ method public static double max(double, double);
method public static int min(int, int);
method public static long min(long, long);
+ method public static float min(float, float);
+ method public static double min(double, double);
method public static double nextAfter(double, double);
method public static float nextAfter(float, double);
method public static double nextUp(double);
@@ -44645,8 +44939,8 @@
method public static double pow(double, double);
method public static double random();
method public static double rint(double);
- method public static long round(double);
method public static int round(float);
+ method public static long round(double);
method public static double scalb(double, int);
method public static float scalb(float, int);
method public static double signum(double);
@@ -44724,9 +45018,9 @@
method public final void notify();
method public final void notifyAll();
method public java.lang.String toString();
- method public final void wait() throws java.lang.InterruptedException;
method public final void wait(long) throws java.lang.InterruptedException;
method public final void wait(long, int) throws java.lang.InterruptedException;
+ method public final void wait() throws java.lang.InterruptedException;
}
public class OutOfMemoryError extends java.lang.VirtualMachineError {
@@ -44767,19 +45061,49 @@
}
public final class ProcessBuilder {
- ctor public ProcessBuilder(java.lang.String...);
ctor public ProcessBuilder(java.util.List<java.lang.String>);
- method public java.util.List<java.lang.String> command();
- method public java.lang.ProcessBuilder command(java.lang.String...);
+ ctor public ProcessBuilder(java.lang.String...);
method public java.lang.ProcessBuilder command(java.util.List<java.lang.String>);
+ method public java.lang.ProcessBuilder command(java.lang.String...);
+ method public java.util.List<java.lang.String> command();
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
+ method public java.lang.ProcessBuilder inheritIO();
+ method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectError(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
+ method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectInput(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectInput();
+ method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectOutput(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
+ public static abstract class ProcessBuilder.Redirect {
+ method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
+ method public java.io.File file();
+ method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
+ method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
+ method public abstract java.lang.ProcessBuilder.Redirect.Type type();
+ field public static final java.lang.ProcessBuilder.Redirect INHERIT;
+ field public static final java.lang.ProcessBuilder.Redirect PIPE;
+ }
+
+ public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
+ method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
+ method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
+ }
+
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
@@ -44787,8 +45111,8 @@
public class ReflectiveOperationException extends java.lang.Exception {
ctor public ReflectiveOperationException();
ctor public ReflectiveOperationException(java.lang.String);
- ctor public ReflectiveOperationException(java.lang.Throwable);
ctor public ReflectiveOperationException(java.lang.String, java.lang.Throwable);
+ ctor public ReflectiveOperationException(java.lang.Throwable);
}
public abstract interface Runnable {
@@ -44798,12 +45122,12 @@
public class Runtime {
method public void addShutdownHook(java.lang.Thread);
method public int availableProcessors();
- method public java.lang.Process exec(java.lang.String[]) throws java.io.IOException;
- method public java.lang.Process exec(java.lang.String[], java.lang.String[]) throws java.io.IOException;
- method public java.lang.Process exec(java.lang.String[], java.lang.String[], java.io.File) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String, java.lang.String[]) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String, java.lang.String[], java.io.File) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[]) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[], java.lang.String[]) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[], java.lang.String[], java.io.File) throws java.io.IOException;
method public void exit(int);
method public long freeMemory();
method public void gc();
@@ -44827,6 +45151,7 @@
ctor public RuntimeException(java.lang.String);
ctor public RuntimeException(java.lang.String, java.lang.Throwable);
ctor public RuntimeException(java.lang.Throwable);
+ ctor protected RuntimeException(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public final class RuntimePermission extends java.security.BasicPermission {
@@ -44891,8 +45216,8 @@
}
public final class Short extends java.lang.Number implements java.lang.Comparable {
- ctor public Short(java.lang.String) throws java.lang.NumberFormatException;
ctor public Short(short);
+ ctor public Short(java.lang.String) throws java.lang.NumberFormatException;
method public static int compare(short, short);
method public int compareTo(java.lang.Short);
method public static java.lang.Short decode(java.lang.String) throws java.lang.NumberFormatException;
@@ -44900,12 +45225,12 @@
method public float floatValue();
method public int intValue();
method public long longValue();
- method public static short parseShort(java.lang.String) throws java.lang.NumberFormatException;
method public static short parseShort(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static short parseShort(java.lang.String) throws java.lang.NumberFormatException;
method public static short reverseBytes(short);
method public static java.lang.String toString(short);
- method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(short);
field public static final short MAX_VALUE = 32767; // 0x7fff
field public static final short MIN_VALUE = -32768; // 0xffff8000
@@ -44929,10 +45254,10 @@
public final class StrictMath {
method public static double IEEEremainder(double, double);
- method public static double abs(double);
- method public static float abs(float);
method public static int abs(int);
method public static long abs(long);
+ method public static float abs(float);
+ method public static double abs(double);
method public static double acos(double);
method public static double asin(double);
method public static double atan(double);
@@ -44952,14 +45277,14 @@
method public static double log(double);
method public static double log10(double);
method public static double log1p(double);
- method public static double max(double, double);
- method public static float max(float, float);
method public static int max(int, int);
method public static long max(long, long);
- method public static double min(double, double);
- method public static float min(float, float);
+ method public static float max(float, float);
+ method public static double max(double, double);
method public static int min(int, int);
method public static long min(long, long);
+ method public static float min(float, float);
+ method public static double min(double, double);
method public static double nextAfter(double, double);
method public static float nextAfter(float, double);
method public static double nextUp(double);
@@ -44967,8 +45292,8 @@
method public static double pow(double, double);
method public static double random();
method public static double rint(double);
- method public static long round(double);
method public static int round(float);
+ method public static long round(double);
method public static double scalb(double, int);
method public static float scalb(float, int);
method public static double signum(double);
@@ -44988,19 +45313,19 @@
public final class String implements java.lang.CharSequence java.lang.Comparable java.io.Serializable {
ctor public String();
- ctor public String(byte[]);
- ctor public deprecated String(byte[], int);
- ctor public String(byte[], int, int);
- ctor public deprecated String(byte[], int, int, int);
- ctor public String(byte[], int, int, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public String(byte[], java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public String(byte[], int, int, java.nio.charset.Charset);
- ctor public String(byte[], java.nio.charset.Charset);
+ ctor public String(java.lang.String);
ctor public String(char[]);
ctor public String(char[], int, int);
- ctor public String(java.lang.String);
- ctor public String(java.lang.StringBuffer);
ctor public String(int[], int, int);
+ ctor public deprecated String(byte[], int, int, int);
+ ctor public deprecated String(byte[], int);
+ ctor public String(byte[], int, int, java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public String(byte[], int, int, java.nio.charset.Charset);
+ ctor public String(byte[], java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public String(byte[], java.nio.charset.Charset);
+ ctor public String(byte[], int, int);
+ ctor public String(byte[]);
+ ctor public String(java.lang.StringBuffer);
ctor public String(java.lang.StringBuilder);
method public char charAt(int);
method public int codePointAt(int);
@@ -45012,16 +45337,16 @@
method public boolean contains(java.lang.CharSequence);
method public boolean contentEquals(java.lang.StringBuffer);
method public boolean contentEquals(java.lang.CharSequence);
- method public static java.lang.String copyValueOf(char[]);
method public static java.lang.String copyValueOf(char[], int, int);
+ method public static java.lang.String copyValueOf(char[]);
method public boolean endsWith(java.lang.String);
method public boolean equalsIgnoreCase(java.lang.String);
method public static java.lang.String format(java.lang.String, java.lang.Object...);
method public static java.lang.String format(java.util.Locale, java.lang.String, java.lang.Object...);
method public deprecated void getBytes(int, int, byte[], int);
- method public byte[] getBytes();
method public byte[] getBytes(java.lang.String) throws java.io.UnsupportedEncodingException;
method public byte[] getBytes(java.nio.charset.Charset);
+ method public byte[] getBytes();
method public void getChars(int, int, char[], int);
method public int indexOf(int);
method public int indexOf(int, int);
@@ -45042,109 +45367,51 @@
method public java.lang.String replace(java.lang.CharSequence, java.lang.CharSequence);
method public java.lang.String replaceAll(java.lang.String, java.lang.String);
method public java.lang.String replaceFirst(java.lang.String, java.lang.String);
- method public java.lang.String[] split(java.lang.String);
method public java.lang.String[] split(java.lang.String, int);
- method public boolean startsWith(java.lang.String);
+ method public java.lang.String[] split(java.lang.String);
method public boolean startsWith(java.lang.String, int);
+ method public boolean startsWith(java.lang.String);
method public java.lang.CharSequence subSequence(int, int);
method public java.lang.String substring(int);
method public java.lang.String substring(int, int);
method public char[] toCharArray();
- method public java.lang.String toLowerCase();
method public java.lang.String toLowerCase(java.util.Locale);
- method public java.lang.String toUpperCase();
+ method public java.lang.String toLowerCase();
method public java.lang.String toUpperCase(java.util.Locale);
+ method public java.lang.String toUpperCase();
method public java.lang.String trim();
+ method public static java.lang.String valueOf(java.lang.Object);
method public static java.lang.String valueOf(char[]);
method public static java.lang.String valueOf(char[], int, int);
+ method public static java.lang.String valueOf(boolean);
method public static java.lang.String valueOf(char);
- method public static java.lang.String valueOf(double);
- method public static java.lang.String valueOf(float);
method public static java.lang.String valueOf(int);
method public static java.lang.String valueOf(long);
- method public static java.lang.String valueOf(java.lang.Object);
- method public static java.lang.String valueOf(boolean);
+ method public static java.lang.String valueOf(float);
+ method public static java.lang.String valueOf(double);
field public static final java.util.Comparator<java.lang.String> CASE_INSENSITIVE_ORDER;
}
- public final class StringBuffer extends java.lang.AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+ public final class StringBuffer extends java.lang.AbstractStringBuilder implements java.lang.CharSequence java.io.Serializable {
ctor public StringBuffer();
ctor public StringBuffer(int);
ctor public StringBuffer(java.lang.String);
ctor public StringBuffer(java.lang.CharSequence);
- method public java.lang.StringBuffer append(boolean);
- method public synchronized java.lang.StringBuffer append(char);
- method public java.lang.StringBuffer append(double);
- method public java.lang.StringBuffer append(float);
- method public java.lang.StringBuffer append(int);
- method public java.lang.StringBuffer append(long);
- method public synchronized java.lang.StringBuffer append(java.lang.Object);
- method public synchronized java.lang.StringBuffer append(java.lang.String);
- method public synchronized java.lang.StringBuffer append(java.lang.StringBuffer);
- method public synchronized java.lang.StringBuffer append(char[]);
- method public synchronized java.lang.StringBuffer append(char[], int, int);
- method public synchronized java.lang.StringBuffer append(java.lang.CharSequence);
- method public synchronized java.lang.StringBuffer append(java.lang.CharSequence, int, int);
- method public java.lang.StringBuffer appendCodePoint(int);
- method public synchronized java.lang.StringBuffer delete(int, int);
- method public synchronized java.lang.StringBuffer deleteCharAt(int);
- method public synchronized java.lang.StringBuffer insert(int, char);
- method public java.lang.StringBuffer insert(int, boolean);
- method public java.lang.StringBuffer insert(int, int);
- method public java.lang.StringBuffer insert(int, long);
- method public java.lang.StringBuffer insert(int, double);
- method public java.lang.StringBuffer insert(int, float);
- method public java.lang.StringBuffer insert(int, java.lang.Object);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.String);
- method public synchronized java.lang.StringBuffer insert(int, char[]);
- method public synchronized java.lang.StringBuffer insert(int, char[], int, int);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.CharSequence);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.CharSequence, int, int);
- method public synchronized java.lang.StringBuffer replace(int, int, java.lang.String);
- method public synchronized java.lang.StringBuffer reverse();
+ method public synchronized java.lang.String toString();
}
- public final class StringBuilder extends java.lang.AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+ public final class StringBuilder extends java.lang.AbstractStringBuilder implements java.lang.CharSequence java.io.Serializable {
ctor public StringBuilder();
ctor public StringBuilder(int);
- ctor public StringBuilder(java.lang.CharSequence);
ctor public StringBuilder(java.lang.String);
- method public java.lang.StringBuilder append(boolean);
- method public java.lang.StringBuilder append(char);
- method public java.lang.StringBuilder append(int);
- method public java.lang.StringBuilder append(long);
- method public java.lang.StringBuilder append(float);
- method public java.lang.StringBuilder append(double);
- method public java.lang.StringBuilder append(java.lang.Object);
- method public java.lang.StringBuilder append(java.lang.String);
- method public java.lang.StringBuilder append(java.lang.StringBuffer);
- method public java.lang.StringBuilder append(char[]);
- method public java.lang.StringBuilder append(char[], int, int);
- method public java.lang.StringBuilder append(java.lang.CharSequence);
- method public java.lang.StringBuilder append(java.lang.CharSequence, int, int);
- method public java.lang.StringBuilder appendCodePoint(int);
- method public java.lang.StringBuilder delete(int, int);
- method public java.lang.StringBuilder deleteCharAt(int);
- method public java.lang.StringBuilder insert(int, boolean);
- method public java.lang.StringBuilder insert(int, char);
- method public java.lang.StringBuilder insert(int, int);
- method public java.lang.StringBuilder insert(int, long);
- method public java.lang.StringBuilder insert(int, float);
- method public java.lang.StringBuilder insert(int, double);
- method public java.lang.StringBuilder insert(int, java.lang.Object);
- method public java.lang.StringBuilder insert(int, java.lang.String);
- method public java.lang.StringBuilder insert(int, char[]);
- method public java.lang.StringBuilder insert(int, char[], int, int);
- method public java.lang.StringBuilder insert(int, java.lang.CharSequence);
- method public java.lang.StringBuilder insert(int, java.lang.CharSequence, int, int);
- method public java.lang.StringBuilder replace(int, int, java.lang.String);
- method public java.lang.StringBuilder reverse();
+ ctor public StringBuilder(java.lang.CharSequence);
+ method public java.lang.String toString();
}
public class StringIndexOutOfBoundsException extends java.lang.IndexOutOfBoundsException {
ctor public StringIndexOutOfBoundsException();
- ctor public StringIndexOutOfBoundsException(int);
ctor public StringIndexOutOfBoundsException(java.lang.String);
+ ctor public StringIndexOutOfBoundsException(int);
}
public abstract class SuppressWarnings implements java.lang.annotation.Annotation {
@@ -45186,11 +45453,11 @@
public class Thread implements java.lang.Runnable {
ctor public Thread();
ctor public Thread(java.lang.Runnable);
- ctor public Thread(java.lang.Runnable, java.lang.String);
- ctor public Thread(java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable);
- ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String);
+ ctor public Thread(java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.String);
+ ctor public Thread(java.lang.Runnable, java.lang.String);
+ ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long);
method public static int activeCount();
method public final void checkAccess();
@@ -45215,9 +45482,9 @@
method public final boolean isAlive();
method public final boolean isDaemon();
method public boolean isInterrupted();
- method public final void join() throws java.lang.InterruptedException;
method public final void join(long) throws java.lang.InterruptedException;
method public final void join(long, int) throws java.lang.InterruptedException;
+ method public final void join() throws java.lang.InterruptedException;
method public final deprecated void resume();
method public void run();
method public void setContextClassLoader(java.lang.ClassLoader);
@@ -45230,7 +45497,7 @@
method public static void sleep(long, int) throws java.lang.InterruptedException;
method public synchronized void start();
method public final deprecated void stop();
- method public final deprecated synchronized void stop(java.lang.Throwable);
+ method public final deprecated void stop(java.lang.Throwable);
method public final deprecated void suspend();
method public static void yield();
field public static final int MAX_PRIORITY = 10; // 0xa
@@ -45299,14 +45566,14 @@
ctor public Throwable(java.lang.String, java.lang.Throwable);
ctor public Throwable(java.lang.Throwable);
ctor protected Throwable(java.lang.String, java.lang.Throwable, boolean, boolean);
- method public final void addSuppressed(java.lang.Throwable);
- method public java.lang.Throwable fillInStackTrace();
- method public java.lang.Throwable getCause();
+ method public final synchronized void addSuppressed(java.lang.Throwable);
+ method public synchronized java.lang.Throwable fillInStackTrace();
+ method public synchronized java.lang.Throwable getCause();
method public java.lang.String getLocalizedMessage();
method public java.lang.String getMessage();
method public java.lang.StackTraceElement[] getStackTrace();
- method public final java.lang.Throwable[] getSuppressed();
- method public java.lang.Throwable initCause(java.lang.Throwable);
+ method public final synchronized java.lang.Throwable[] getSuppressed();
+ method public synchronized java.lang.Throwable initCause(java.lang.Throwable);
method public void printStackTrace();
method public void printStackTrace(java.io.PrintStream);
method public void printStackTrace(java.io.PrintWriter);
@@ -45427,15 +45694,16 @@
public abstract class Reference {
method public void clear();
method public boolean enqueue();
+ method public final synchronized boolean enqueueInternal();
method public T get();
method public boolean isEnqueued();
}
public class ReferenceQueue {
ctor public ReferenceQueue();
- method public synchronized java.lang.ref.Reference<? extends T> poll();
+ method public java.lang.ref.Reference<? extends T> poll();
+ method public java.lang.ref.Reference<? extends T> remove(long) throws java.lang.IllegalArgumentException, java.lang.InterruptedException;
method public java.lang.ref.Reference<? extends T> remove() throws java.lang.InterruptedException;
- method public synchronized java.lang.ref.Reference<? extends T> remove(long) throws java.lang.InterruptedException;
}
public class SoftReference extends java.lang.ref.Reference {
@@ -45459,8 +45727,8 @@
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public boolean isAccessible();
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
- method public void setAccessible(boolean);
- method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean);
+ method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
+ method public void setAccessible(boolean) throws java.lang.SecurityException;
}
public abstract interface AnnotatedElement {
@@ -45481,8 +45749,9 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
+ method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
+ method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static void setBoolean(java.lang.Object, int, boolean);
method public static void setByte(java.lang.Object, int, byte) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -45497,7 +45766,6 @@
public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
- method public java.lang.annotation.Annotation[] getAnnotations();
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<T> getDeclaringClass();
method public java.lang.Class<?>[] getExceptionTypes();
@@ -45584,7 +45852,6 @@
public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
- method public java.lang.annotation.Annotation[] getAnnotations();
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Object getDefaultValue();
@@ -45865,10 +46132,10 @@
method protected final java.net.InetAddress getRequestingSite();
method protected java.net.URL getRequestingURL();
method protected java.net.Authenticator.RequestorType getRequestorType();
- method public static synchronized java.net.PasswordAuthentication requestPasswordAuthentication(java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
- method public static synchronized java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
+ method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
+ method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String, java.net.URL, java.net.Authenticator.RequestorType);
- method public static void setDefault(java.net.Authenticator);
+ method public static synchronized void setDefault(java.net.Authenticator);
}
public static final class Authenticator.RequestorType extends java.lang.Enum {
@@ -45879,8 +46146,9 @@
}
public class BindException extends java.net.SocketException {
- ctor public BindException();
ctor public BindException(java.lang.String);
+ ctor public BindException();
+ ctor public BindException(java.lang.String, java.lang.Throwable);
}
public abstract class CacheRequest {
@@ -45896,8 +46164,9 @@
}
public class ConnectException extends java.net.SocketException {
- ctor public ConnectException();
ctor public ConnectException(java.lang.String);
+ ctor public ConnectException();
+ ctor public ConnectException(java.lang.String, java.lang.Throwable);
}
public abstract class ContentHandler {
@@ -45913,9 +46182,9 @@
public abstract class CookieHandler {
ctor public CookieHandler();
method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> get(java.net.URI, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static java.net.CookieHandler getDefault();
+ method public static synchronized java.net.CookieHandler getDefault();
method public abstract void put(java.net.URI, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static void setDefault(java.net.CookieHandler);
+ method public static synchronized void setDefault(java.net.CookieHandler);
}
public class CookieManager extends java.net.CookieHandler {
@@ -45944,12 +46213,12 @@
}
public final class DatagramPacket {
- ctor public DatagramPacket(byte[], int);
ctor public DatagramPacket(byte[], int, int);
+ ctor public DatagramPacket(byte[], int);
ctor public DatagramPacket(byte[], int, int, java.net.InetAddress, int);
+ ctor public DatagramPacket(byte[], int, int, java.net.SocketAddress) throws java.net.SocketException;
ctor public DatagramPacket(byte[], int, java.net.InetAddress, int);
ctor public DatagramPacket(byte[], int, java.net.SocketAddress) throws java.net.SocketException;
- ctor public DatagramPacket(byte[], int, int, java.net.SocketAddress) throws java.net.SocketException;
method public synchronized java.net.InetAddress getAddress();
method public synchronized byte[] getData();
method public synchronized int getLength();
@@ -45966,17 +46235,18 @@
public class DatagramSocket implements java.io.Closeable {
ctor public DatagramSocket() throws java.net.SocketException;
- ctor public DatagramSocket(int) throws java.net.SocketException;
- ctor public DatagramSocket(int, java.net.InetAddress) throws java.net.SocketException;
ctor protected DatagramSocket(java.net.DatagramSocketImpl);
ctor public DatagramSocket(java.net.SocketAddress) throws java.net.SocketException;
- method public void bind(java.net.SocketAddress) throws java.net.SocketException;
+ ctor public DatagramSocket(int) throws java.net.SocketException;
+ ctor public DatagramSocket(int, java.net.InetAddress) throws java.net.SocketException;
+ method public synchronized void bind(java.net.SocketAddress) throws java.net.SocketException;
method public void close();
- method public void connect(java.net.SocketAddress) throws java.net.SocketException;
method public void connect(java.net.InetAddress, int);
+ method public void connect(java.net.SocketAddress) throws java.net.SocketException;
method public void disconnect();
- method public boolean getBroadcast() throws java.net.SocketException;
+ method public synchronized boolean getBroadcast() throws java.net.SocketException;
method public java.nio.channels.DatagramChannel getChannel();
+ method public final java.io.FileDescriptor getFileDescriptor$();
method public java.net.InetAddress getInetAddress();
method public java.net.InetAddress getLocalAddress();
method public int getLocalPort();
@@ -45984,22 +46254,22 @@
method public int getPort();
method public synchronized int getReceiveBufferSize() throws java.net.SocketException;
method public java.net.SocketAddress getRemoteSocketAddress();
- method public boolean getReuseAddress() throws java.net.SocketException;
+ method public synchronized boolean getReuseAddress() throws java.net.SocketException;
method public synchronized int getSendBufferSize() throws java.net.SocketException;
method public synchronized int getSoTimeout() throws java.net.SocketException;
- method public int getTrafficClass() throws java.net.SocketException;
+ method public synchronized int getTrafficClass() throws java.net.SocketException;
method public boolean isBound();
method public boolean isClosed();
method public boolean isConnected();
method public synchronized void receive(java.net.DatagramPacket) throws java.io.IOException;
method public void send(java.net.DatagramPacket) throws java.io.IOException;
- method public void setBroadcast(boolean) throws java.net.SocketException;
+ method public synchronized void setBroadcast(boolean) throws java.net.SocketException;
method public static synchronized void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory) throws java.io.IOException;
method public synchronized void setReceiveBufferSize(int) throws java.net.SocketException;
- method public void setReuseAddress(boolean) throws java.net.SocketException;
+ method public synchronized void setReuseAddress(boolean) throws java.net.SocketException;
method public synchronized void setSendBufferSize(int) throws java.net.SocketException;
method public synchronized void setSoTimeout(int) throws java.net.SocketException;
- method public void setTrafficClass(int) throws java.net.SocketException;
+ method public synchronized void setTrafficClass(int) throws java.net.SocketException;
}
public abstract class DatagramSocketImpl implements java.net.SocketOptions {
@@ -46051,11 +46321,14 @@
method public java.lang.String getValue();
method public int getVersion();
method public boolean hasExpired();
+ method public boolean isHttpOnly();
method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
+ method public static java.util.List<java.net.HttpCookie> parse(java.lang.String, boolean);
method public void setComment(java.lang.String);
method public void setCommentURL(java.lang.String);
method public void setDiscard(boolean);
method public void setDomain(java.lang.String);
+ method public void setHttpOnly(boolean);
method public void setMaxAge(long);
method public void setPath(java.lang.String);
method public void setPortlist(java.lang.String);
@@ -46082,8 +46355,8 @@
method public int getResponseCode() throws java.io.IOException;
method public java.lang.String getResponseMessage() throws java.io.IOException;
method public void setChunkedStreamingMode(int);
- method public void setFixedLengthStreamingMode(long);
method public void setFixedLengthStreamingMode(int);
+ method public void setFixedLengthStreamingMode(long);
method public static void setFollowRedirects(boolean);
method public void setInstanceFollowRedirects(boolean);
method public void setRequestMethod(java.lang.String) throws java.net.ProtocolException;
@@ -46143,21 +46416,27 @@
}
public final class Inet4Address extends java.net.InetAddress {
+ field public static final java.net.InetAddress ALL;
+ field public static final java.net.InetAddress ANY;
+ field public static final java.net.InetAddress LOOPBACK;
}
public final class Inet6Address extends java.net.InetAddress {
- method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], int) throws java.net.UnknownHostException;
method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], java.net.NetworkInterface) throws java.net.UnknownHostException;
+ method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], int) throws java.net.UnknownHostException;
method public int getScopeId();
method public java.net.NetworkInterface getScopedInterface();
method public boolean isIPv4CompatibleAddress();
+ field public static final java.net.InetAddress ANY;
+ field public static final java.net.InetAddress LOOPBACK;
}
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
+ method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
- method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
+ method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
method public java.lang.String getCanonicalHostName();
method public java.lang.String getHostAddress();
@@ -46264,8 +46543,8 @@
}
public class NoRouteToHostException extends java.net.SocketException {
- ctor public NoRouteToHostException();
ctor public NoRouteToHostException(java.lang.String);
+ ctor public NoRouteToHostException();
}
public final class PasswordAuthentication {
@@ -46275,13 +46554,19 @@
}
public class PortUnreachableException extends java.net.SocketException {
- ctor public PortUnreachableException();
ctor public PortUnreachableException(java.lang.String);
+ ctor public PortUnreachableException();
+ ctor public PortUnreachableException(java.lang.String, java.lang.Throwable);
}
public class ProtocolException extends java.io.IOException {
- ctor public ProtocolException();
ctor public ProtocolException(java.lang.String);
+ ctor public ProtocolException();
+ ctor public ProtocolException(java.lang.String, java.lang.Throwable);
+ }
+
+ public abstract interface ProtocolFamily {
+ method public abstract java.lang.String name();
}
public class Proxy {
@@ -46312,9 +46597,9 @@
public abstract class ResponseCache {
ctor public ResponseCache();
method public abstract java.net.CacheResponse get(java.net.URI, java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static java.net.ResponseCache getDefault();
+ method public static synchronized java.net.ResponseCache getDefault();
method public abstract java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException;
- method public static void setDefault(java.net.ResponseCache);
+ method public static synchronized void setDefault(java.net.ResponseCache);
}
public abstract class SecureCacheResponse extends java.net.CacheResponse {
@@ -46339,14 +46624,14 @@
method public java.net.InetAddress getInetAddress();
method public int getLocalPort();
method public java.net.SocketAddress getLocalSocketAddress();
- method public int getReceiveBufferSize() throws java.net.SocketException;
+ method public synchronized int getReceiveBufferSize() throws java.net.SocketException;
method public boolean getReuseAddress() throws java.net.SocketException;
method public synchronized int getSoTimeout() throws java.io.IOException;
method protected final void implAccept(java.net.Socket) throws java.io.IOException;
method public boolean isBound();
method public boolean isClosed();
method public void setPerformancePreferences(int, int, int);
- method public void setReceiveBufferSize(int) throws java.net.SocketException;
+ method public synchronized void setReceiveBufferSize(int) throws java.net.SocketException;
method public void setReuseAddress(boolean) throws java.net.SocketException;
method public synchronized void setSoTimeout(int) throws java.net.SocketException;
method public static synchronized void setSocketFactory(java.net.SocketImplFactory) throws java.io.IOException;
@@ -46355,13 +46640,13 @@
public class Socket implements java.io.Closeable {
ctor public Socket();
ctor public Socket(java.net.Proxy);
- ctor public Socket(java.lang.String, int) throws java.io.IOException, java.net.UnknownHostException;
- ctor public Socket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException;
- ctor public deprecated Socket(java.lang.String, int, boolean) throws java.io.IOException;
- ctor public Socket(java.net.InetAddress, int) throws java.io.IOException;
- ctor public Socket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
- ctor public deprecated Socket(java.net.InetAddress, int, boolean) throws java.io.IOException;
ctor protected Socket(java.net.SocketImpl) throws java.net.SocketException;
+ ctor public Socket(java.lang.String, int) throws java.io.IOException, java.net.UnknownHostException;
+ ctor public Socket(java.net.InetAddress, int) throws java.io.IOException;
+ ctor public Socket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException;
+ ctor public Socket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+ ctor public deprecated Socket(java.lang.String, int, boolean) throws java.io.IOException;
+ ctor public deprecated Socket(java.net.InetAddress, int, boolean) throws java.io.IOException;
method public void bind(java.net.SocketAddress) throws java.io.IOException;
method public synchronized void close() throws java.io.IOException;
method public void connect(java.net.SocketAddress) throws java.io.IOException;
@@ -46410,8 +46695,10 @@
}
public class SocketException extends java.io.IOException {
- ctor public SocketException();
ctor public SocketException(java.lang.String);
+ ctor public SocketException();
+ ctor public SocketException(java.lang.Throwable);
+ ctor public SocketException(java.lang.String, java.lang.Throwable);
}
public abstract class SocketImpl implements java.net.SocketOptions {
@@ -46424,7 +46711,7 @@
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method protected java.io.FileDescriptor getFileDescriptor();
+ method public java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -46446,6 +46733,11 @@
method public abstract java.net.SocketImpl createSocketImpl();
}
+ public abstract interface SocketOption {
+ method public abstract java.lang.String name();
+ method public abstract java.lang.Class<T> type();
+ }
+
public abstract interface SocketOptions {
method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -46467,21 +46759,46 @@
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
public class SocketTimeoutException extends java.io.InterruptedIOException {
- ctor public SocketTimeoutException();
ctor public SocketTimeoutException(java.lang.String);
+ ctor public SocketTimeoutException();
+ ctor public SocketTimeoutException(java.lang.Throwable);
+ ctor public SocketTimeoutException(java.lang.String, java.lang.Throwable);
+ }
+
+ public final class StandardProtocolFamily extends java.lang.Enum implements java.net.ProtocolFamily {
+ method public static java.net.StandardProtocolFamily valueOf(java.lang.String);
+ method public static final java.net.StandardProtocolFamily[] values();
+ enum_constant public static final java.net.StandardProtocolFamily INET;
+ enum_constant public static final java.net.StandardProtocolFamily INET6;
+ }
+
+ public final class StandardSocketOptions {
+ field public static final java.net.SocketOption<java.net.NetworkInterface> IP_MULTICAST_IF;
+ field public static final java.net.SocketOption<java.lang.Boolean> IP_MULTICAST_LOOP;
+ field public static final java.net.SocketOption<java.lang.Integer> IP_MULTICAST_TTL;
+ field public static final java.net.SocketOption<java.lang.Integer> IP_TOS;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_BROADCAST;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_KEEPALIVE;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_LINGER;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_RCVBUF;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_REUSEADDR;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_SNDBUF;
+ field public static final java.net.SocketOption<java.lang.Boolean> TCP_NODELAY;
}
public final class URI implements java.lang.Comparable java.io.Serializable {
ctor public URI(java.lang.String) throws java.net.URISyntaxException;
- ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
- ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
+ ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
+ ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
method public int compareTo(java.net.URI);
method public static java.net.URI create(java.lang.String);
method public java.lang.String getAuthority();
@@ -46519,12 +46836,12 @@
}
public final class URL implements java.io.Serializable {
+ ctor public URL(java.lang.String, java.lang.String, int, java.lang.String) throws java.net.MalformedURLException;
+ ctor public URL(java.lang.String, java.lang.String, java.lang.String) throws java.net.MalformedURLException;
+ ctor public URL(java.lang.String, java.lang.String, int, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
ctor public URL(java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, java.lang.String) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, int, java.lang.String) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, int, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
method public java.lang.String getAuthority();
method public final java.lang.Object getContent() throws java.io.IOException;
method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
@@ -46543,22 +46860,24 @@
method public boolean sameFile(java.net.URL);
method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public static synchronized void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
+ method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
method public java.net.URI toURI() throws java.net.URISyntaxException;
+ method public java.net.URI toURILenient() throws java.net.URISyntaxException;
}
- public class URLClassLoader extends java.security.SecureClassLoader {
- ctor public URLClassLoader(java.net.URL[]);
+ public class URLClassLoader extends java.security.SecureClassLoader implements java.io.Closeable {
ctor public URLClassLoader(java.net.URL[], java.lang.ClassLoader);
+ ctor public URLClassLoader(java.net.URL[]);
ctor public URLClassLoader(java.net.URL[], java.lang.ClassLoader, java.net.URLStreamHandlerFactory);
method protected void addURL(java.net.URL);
+ method public void close() throws java.io.IOException;
method protected java.lang.Package definePackage(java.lang.String, java.util.jar.Manifest, java.net.URL) throws java.lang.IllegalArgumentException;
method public java.net.URL findResource(java.lang.String);
method public java.util.Enumeration<java.net.URL> findResources(java.lang.String) throws java.io.IOException;
method public java.net.URL[] getURLs();
- method public static java.net.URLClassLoader newInstance(java.net.URL[]);
method public static java.net.URLClassLoader newInstance(java.net.URL[], java.lang.ClassLoader);
+ method public static java.net.URLClassLoader newInstance(java.net.URL[]);
}
public abstract class URLConnection {
@@ -46571,6 +46890,7 @@
method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public java.lang.String getContentEncoding();
method public int getContentLength();
+ method public long getContentLengthLong();
method public java.lang.String getContentType();
method public long getDate();
method public static boolean getDefaultAllowUserInteraction();
@@ -46579,12 +46899,13 @@
method public boolean getDoInput();
method public boolean getDoOutput();
method public long getExpiration();
- method public static java.net.FileNameMap getFileNameMap();
- method public java.lang.String getHeaderField(int);
+ method public static synchronized java.net.FileNameMap getFileNameMap();
method public java.lang.String getHeaderField(java.lang.String);
+ method public java.lang.String getHeaderField(int);
method public long getHeaderFieldDate(java.lang.String, long);
method public int getHeaderFieldInt(java.lang.String, int);
method public java.lang.String getHeaderFieldKey(int);
+ method public long getHeaderFieldLong(java.lang.String, long);
method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
method public long getIfModifiedSince();
method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -46635,15 +46956,15 @@
ctor public URLStreamHandler();
method protected boolean equals(java.net.URL, java.net.URL);
method protected int getDefaultPort();
- method protected java.net.InetAddress getHostAddress(java.net.URL);
+ method protected synchronized java.net.InetAddress getHostAddress(java.net.URL);
method protected int hashCode(java.net.URL);
method protected boolean hostsEqual(java.net.URL, java.net.URL);
method protected abstract java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
method protected java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
method protected void parseURL(java.net.URL, java.lang.String, int, int);
method protected boolean sameFile(java.net.URL, java.net.URL);
- method protected deprecated void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method protected deprecated void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected java.lang.String toExternalForm(java.net.URL);
}
@@ -46652,8 +46973,8 @@
}
public class UnknownHostException extends java.io.IOException {
- ctor public UnknownHostException();
ctor public UnknownHostException(java.lang.String);
+ ctor public UnknownHostException();
}
public class UnknownServiceException extends java.io.IOException {
@@ -46709,9 +47030,9 @@
method public int compareTo(java.nio.ByteBuffer);
method public abstract java.nio.ByteBuffer duplicate();
method public abstract byte get();
- method public java.nio.ByteBuffer get(byte[]);
- method public java.nio.ByteBuffer get(byte[], int, int);
method public abstract byte get(int);
+ method public java.nio.ByteBuffer get(byte[], int, int);
+ method public java.nio.ByteBuffer get(byte[]);
method public abstract char getChar();
method public abstract char getChar(int);
method public abstract double getDouble();
@@ -46729,10 +47050,10 @@
method public final java.nio.ByteOrder order();
method public final java.nio.ByteBuffer order(java.nio.ByteOrder);
method public abstract java.nio.ByteBuffer put(byte);
- method public final java.nio.ByteBuffer put(byte[]);
- method public java.nio.ByteBuffer put(byte[], int, int);
- method public java.nio.ByteBuffer put(java.nio.ByteBuffer);
method public abstract java.nio.ByteBuffer put(int, byte);
+ method public java.nio.ByteBuffer put(java.nio.ByteBuffer);
+ method public java.nio.ByteBuffer put(byte[], int, int);
+ method public final java.nio.ByteBuffer put(byte[]);
method public abstract java.nio.ByteBuffer putChar(char);
method public abstract java.nio.ByteBuffer putChar(int, char);
method public abstract java.nio.ByteBuffer putDouble(double);
@@ -46746,8 +47067,8 @@
method public abstract java.nio.ByteBuffer putShort(short);
method public abstract java.nio.ByteBuffer putShort(int, short);
method public abstract java.nio.ByteBuffer slice();
- method public static java.nio.ByteBuffer wrap(byte[]);
method public static java.nio.ByteBuffer wrap(byte[], int, int);
+ method public static java.nio.ByteBuffer wrap(byte[]);
}
public final class ByteOrder {
@@ -46758,9 +47079,9 @@
public abstract class CharBuffer extends java.nio.Buffer implements java.lang.Appendable java.lang.CharSequence java.lang.Comparable java.lang.Readable {
method public static java.nio.CharBuffer allocate(int);
- method public java.nio.CharBuffer append(char);
method public java.nio.CharBuffer append(java.lang.CharSequence);
method public java.nio.CharBuffer append(java.lang.CharSequence, int, int);
+ method public java.nio.CharBuffer append(char);
method public final char[] array();
method public final int arrayOffset();
method public abstract java.nio.CharBuffer asReadOnlyBuffer();
@@ -46769,27 +47090,27 @@
method public int compareTo(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer duplicate();
method public abstract char get();
- method public java.nio.CharBuffer get(char[]);
- method public java.nio.CharBuffer get(char[], int, int);
method public abstract char get(int);
+ method public java.nio.CharBuffer get(char[], int, int);
+ method public java.nio.CharBuffer get(char[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public final int length();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.CharBuffer put(char);
- method public final java.nio.CharBuffer put(char[]);
- method public java.nio.CharBuffer put(char[], int, int);
- method public java.nio.CharBuffer put(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer put(int, char);
- method public final java.nio.CharBuffer put(java.lang.String);
+ method public java.nio.CharBuffer put(java.nio.CharBuffer);
+ method public java.nio.CharBuffer put(char[], int, int);
+ method public final java.nio.CharBuffer put(char[]);
method public java.nio.CharBuffer put(java.lang.String, int, int);
+ method public final java.nio.CharBuffer put(java.lang.String);
method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public abstract java.nio.CharBuffer slice();
method public abstract java.nio.CharBuffer subSequence(int, int);
- method public static java.nio.CharBuffer wrap(char[]);
method public static java.nio.CharBuffer wrap(char[], int, int);
- method public static java.nio.CharBuffer wrap(java.lang.CharSequence);
+ method public static java.nio.CharBuffer wrap(char[]);
method public static java.nio.CharBuffer wrap(java.lang.CharSequence, int, int);
+ method public static java.nio.CharBuffer wrap(java.lang.CharSequence);
}
public abstract class DoubleBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -46801,20 +47122,20 @@
method public int compareTo(java.nio.DoubleBuffer);
method public abstract java.nio.DoubleBuffer duplicate();
method public abstract double get();
- method public java.nio.DoubleBuffer get(double[]);
- method public java.nio.DoubleBuffer get(double[], int, int);
method public abstract double get(int);
+ method public java.nio.DoubleBuffer get(double[], int, int);
+ method public java.nio.DoubleBuffer get(double[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.DoubleBuffer put(double);
- method public final java.nio.DoubleBuffer put(double[]);
- method public java.nio.DoubleBuffer put(double[], int, int);
- method public java.nio.DoubleBuffer put(java.nio.DoubleBuffer);
method public abstract java.nio.DoubleBuffer put(int, double);
+ method public java.nio.DoubleBuffer put(java.nio.DoubleBuffer);
+ method public java.nio.DoubleBuffer put(double[], int, int);
+ method public final java.nio.DoubleBuffer put(double[]);
method public abstract java.nio.DoubleBuffer slice();
- method public static java.nio.DoubleBuffer wrap(double[]);
method public static java.nio.DoubleBuffer wrap(double[], int, int);
+ method public static java.nio.DoubleBuffer wrap(double[]);
}
public abstract class FloatBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -46826,20 +47147,20 @@
method public int compareTo(java.nio.FloatBuffer);
method public abstract java.nio.FloatBuffer duplicate();
method public abstract float get();
- method public java.nio.FloatBuffer get(float[]);
- method public java.nio.FloatBuffer get(float[], int, int);
method public abstract float get(int);
+ method public java.nio.FloatBuffer get(float[], int, int);
+ method public java.nio.FloatBuffer get(float[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.FloatBuffer put(float);
- method public final java.nio.FloatBuffer put(float[]);
- method public java.nio.FloatBuffer put(float[], int, int);
- method public java.nio.FloatBuffer put(java.nio.FloatBuffer);
method public abstract java.nio.FloatBuffer put(int, float);
+ method public java.nio.FloatBuffer put(java.nio.FloatBuffer);
+ method public java.nio.FloatBuffer put(float[], int, int);
+ method public final java.nio.FloatBuffer put(float[]);
method public abstract java.nio.FloatBuffer slice();
- method public static java.nio.FloatBuffer wrap(float[]);
method public static java.nio.FloatBuffer wrap(float[], int, int);
+ method public static java.nio.FloatBuffer wrap(float[]);
}
public abstract class IntBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -46851,20 +47172,20 @@
method public int compareTo(java.nio.IntBuffer);
method public abstract java.nio.IntBuffer duplicate();
method public abstract int get();
- method public java.nio.IntBuffer get(int[]);
- method public java.nio.IntBuffer get(int[], int, int);
method public abstract int get(int);
+ method public java.nio.IntBuffer get(int[], int, int);
+ method public java.nio.IntBuffer get(int[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.IntBuffer put(int);
- method public final java.nio.IntBuffer put(int[]);
- method public java.nio.IntBuffer put(int[], int, int);
- method public java.nio.IntBuffer put(java.nio.IntBuffer);
method public abstract java.nio.IntBuffer put(int, int);
+ method public java.nio.IntBuffer put(java.nio.IntBuffer);
+ method public java.nio.IntBuffer put(int[], int, int);
+ method public final java.nio.IntBuffer put(int[]);
method public abstract java.nio.IntBuffer slice();
- method public static java.nio.IntBuffer wrap(int[]);
method public static java.nio.IntBuffer wrap(int[], int, int);
+ method public static java.nio.IntBuffer wrap(int[]);
}
public class InvalidMarkException extends java.lang.IllegalStateException {
@@ -46880,20 +47201,20 @@
method public int compareTo(java.nio.LongBuffer);
method public abstract java.nio.LongBuffer duplicate();
method public abstract long get();
- method public java.nio.LongBuffer get(long[]);
- method public java.nio.LongBuffer get(long[], int, int);
method public abstract long get(int);
+ method public java.nio.LongBuffer get(long[], int, int);
+ method public java.nio.LongBuffer get(long[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.LongBuffer put(long);
- method public final java.nio.LongBuffer put(long[]);
- method public java.nio.LongBuffer put(long[], int, int);
- method public java.nio.LongBuffer put(java.nio.LongBuffer);
method public abstract java.nio.LongBuffer put(int, long);
+ method public java.nio.LongBuffer put(java.nio.LongBuffer);
+ method public java.nio.LongBuffer put(long[], int, int);
+ method public final java.nio.LongBuffer put(long[]);
method public abstract java.nio.LongBuffer slice();
- method public static java.nio.LongBuffer wrap(long[]);
method public static java.nio.LongBuffer wrap(long[], int, int);
+ method public static java.nio.LongBuffer wrap(long[]);
}
public abstract class MappedByteBuffer extends java.nio.ByteBuffer {
@@ -46915,34 +47236,119 @@
method public int compareTo(java.nio.ShortBuffer);
method public abstract java.nio.ShortBuffer duplicate();
method public abstract short get();
- method public java.nio.ShortBuffer get(short[]);
- method public java.nio.ShortBuffer get(short[], int, int);
method public abstract short get(int);
+ method public java.nio.ShortBuffer get(short[], int, int);
+ method public java.nio.ShortBuffer get(short[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.ShortBuffer put(short);
- method public final java.nio.ShortBuffer put(short[]);
- method public java.nio.ShortBuffer put(short[], int, int);
- method public java.nio.ShortBuffer put(java.nio.ShortBuffer);
method public abstract java.nio.ShortBuffer put(int, short);
+ method public java.nio.ShortBuffer put(java.nio.ShortBuffer);
+ method public java.nio.ShortBuffer put(short[], int, int);
+ method public final java.nio.ShortBuffer put(short[]);
method public abstract java.nio.ShortBuffer slice();
- method public static java.nio.ShortBuffer wrap(short[]);
method public static java.nio.ShortBuffer wrap(short[], int, int);
+ method public static java.nio.ShortBuffer wrap(short[]);
}
}
package java.nio.channels {
+ public class AcceptPendingException extends java.lang.IllegalStateException {
+ ctor public AcceptPendingException();
+ }
+
+ public class AlreadyBoundException extends java.lang.IllegalStateException {
+ ctor public AlreadyBoundException();
+ }
+
public class AlreadyConnectedException extends java.lang.IllegalStateException {
ctor public AlreadyConnectedException();
}
+ public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
+ method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
+ method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
+ }
+
+ public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
+ method public abstract void close() throws java.io.IOException;
+ }
+
+ public abstract class AsynchronousChannelGroup {
+ ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public abstract boolean isShutdown();
+ method public abstract boolean isTerminated();
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract void shutdown();
+ method public abstract void shutdownNow() throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
+ }
+
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
+ public abstract class AsynchronousFileChannel implements java.nio.channels.AsynchronousChannel {
+ ctor protected AsynchronousFileChannel();
+ method public abstract void force(boolean) throws java.io.IOException;
+ method public abstract void lock(long, long, boolean, A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock, ? super A>);
+ method public final void lock(A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock, ? super A>);
+ method public abstract java.util.concurrent.Future<java.nio.channels.FileLock> lock(long, long, boolean);
+ method public final java.util.concurrent.Future<java.nio.channels.FileLock> lock();
+ method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public abstract void read(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer, long);
+ method public abstract long size() throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousFileChannel truncate(long) throws java.io.IOException;
+ method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
+ method public abstract void write(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer, long);
+ }
+
+ public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
+ ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
+ method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
+ method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ }
+
+ public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
+ ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
+ method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
+ method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
+ method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
+ method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
+ }
+
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -46959,7 +47365,9 @@
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
+ method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
+ method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -46978,50 +47386,61 @@
ctor public ClosedSelectorException();
}
+ public abstract interface CompletionHandler {
+ method public abstract void completed(V, A);
+ method public abstract void failed(java.lang.Throwable, A);
+ }
+
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
+ method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
method public abstract boolean isConnected();
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
+ method public static java.nio.channels.DatagramChannel open(java.net.ProtocolFamily) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.DatagramSocket socket();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
- public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
ctor protected FileChannel();
method public abstract void force(boolean) throws java.io.IOException;
- method public final java.nio.channels.FileLock lock() throws java.io.IOException;
method public abstract java.nio.channels.FileLock lock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock lock() throws java.io.IOException;
method public abstract java.nio.MappedByteBuffer map(java.nio.channels.FileChannel.MapMode, long, long) throws java.io.IOException;
+ method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
method public abstract long position() throws java.io.IOException;
method public abstract java.nio.channels.FileChannel position(long) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract int read(java.nio.ByteBuffer, long) throws java.io.IOException;
- method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract int read(java.nio.ByteBuffer, long) throws java.io.IOException;
method public abstract long size() throws java.io.IOException;
method public abstract long transferFrom(java.nio.channels.ReadableByteChannel, long, long) throws java.io.IOException;
method public abstract long transferTo(long, long, java.nio.channels.WritableByteChannel) throws java.io.IOException;
method public abstract java.nio.channels.FileChannel truncate(long) throws java.io.IOException;
- method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract int write(java.nio.ByteBuffer, long) throws java.io.IOException;
- method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract int write(java.nio.ByteBuffer, long) throws java.io.IOException;
}
public static class FileChannel.MapMode {
@@ -47032,6 +47451,8 @@
public abstract class FileLock implements java.lang.AutoCloseable {
ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
+ ctor protected FileLock(java.nio.channels.AsynchronousFileChannel, long, long, boolean);
+ method public java.nio.channels.Channel acquiredBy();
method public final java.nio.channels.FileChannel channel();
method public final void close() throws java.io.IOException;
method public final boolean isShared();
@@ -47048,22 +47469,56 @@
}
public abstract interface GatheringByteChannel implements java.nio.channels.WritableByteChannel {
- method public abstract long write(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public abstract long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
public class IllegalBlockingModeException extends java.lang.IllegalStateException {
ctor public IllegalBlockingModeException();
}
+ public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
+ ctor public IllegalChannelGroupException();
+ }
+
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
+ public class InterruptedByTimeoutException extends java.io.IOException {
+ ctor public InterruptedByTimeoutException();
+ }
+
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
+ public abstract class MembershipKey {
+ ctor protected MembershipKey();
+ method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.MulticastChannel channel();
+ method public abstract void drop();
+ method public abstract java.net.InetAddress group();
+ method public abstract boolean isValid();
+ method public abstract java.net.NetworkInterface networkInterface();
+ method public abstract java.net.InetAddress sourceAddress();
+ method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
+ }
+
+ public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
+ }
+
+ public abstract interface NetworkChannel implements java.nio.channels.Channel {
+ method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
+ method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
+ }
+
public class NoConnectionPendingException extends java.lang.IllegalStateException {
ctor public NoConnectionPendingException();
}
@@ -47105,13 +47560,26 @@
method public final int validOps();
}
+ public class ReadPendingException extends java.lang.IllegalStateException {
+ ctor public ReadPendingException();
+ }
+
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
public abstract interface ScatteringByteChannel implements java.nio.channels.ReadableByteChannel {
- method public abstract long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public abstract long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ }
+
+ public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
+ method public abstract long position() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
+ method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
+ method public abstract long size() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
+ method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
@@ -47122,8 +47590,8 @@
method public abstract boolean isRegistered();
method public abstract java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
method public abstract java.nio.channels.spi.SelectorProvider provider();
- method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int) throws java.nio.channels.ClosedChannelException;
method public abstract java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, java.lang.Object) throws java.nio.channels.ClosedChannelException;
+ method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int) throws java.nio.channels.ClosedChannelException;
method public abstract int validOps();
}
@@ -47155,37 +47623,49 @@
method public abstract java.util.Set<java.nio.channels.SelectionKey> keys();
method public static java.nio.channels.Selector open() throws java.io.IOException;
method public abstract java.nio.channels.spi.SelectorProvider provider();
- method public abstract int select() throws java.io.IOException;
method public abstract int select(long) throws java.io.IOException;
+ method public abstract int select() throws java.io.IOException;
method public abstract int selectNow() throws java.io.IOException;
method public abstract java.util.Set<java.nio.channels.SelectionKey> selectedKeys();
method public abstract java.nio.channels.Selector wakeup();
}
- public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
+ public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
+ method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
+ method public abstract java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.ServerSocket socket();
method public final int validOps();
}
- public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
+ ctor public ShutdownChannelGroupException();
+ }
+
+ public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
+ method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean finishConnect() throws java.io.IOException;
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
method public abstract boolean isConnected();
method public abstract boolean isConnectionPending();
method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
method public static java.nio.channels.SocketChannel open(java.net.SocketAddress) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel shutdownInput() throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel shutdownOutput() throws java.io.IOException;
method public abstract java.net.Socket socket();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
public class UnresolvedAddressException extends java.lang.IllegalArgumentException {
@@ -47200,6 +47680,10 @@
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
+ public class WritePendingException extends java.lang.IllegalStateException {
+ ctor public WritePendingException();
+ }
+
}
package java.nio.channels.spi {
@@ -47210,19 +47694,19 @@
method public final void close() throws java.io.IOException;
method protected final void end(boolean) throws java.nio.channels.AsynchronousCloseException;
method protected abstract void implCloseChannel() throws java.io.IOException;
- method public final synchronized boolean isOpen();
+ method public final boolean isOpen();
}
public abstract class AbstractSelectableChannel extends java.nio.channels.SelectableChannel {
ctor protected AbstractSelectableChannel(java.nio.channels.spi.SelectorProvider);
method public final java.lang.Object blockingLock();
method public final java.nio.channels.SelectableChannel configureBlocking(boolean) throws java.io.IOException;
- method protected final synchronized void implCloseChannel() throws java.io.IOException;
+ method protected final void implCloseChannel() throws java.io.IOException;
method protected abstract void implCloseSelectableChannel() throws java.io.IOException;
method protected abstract void implConfigureBlocking(boolean) throws java.io.IOException;
method public final boolean isBlocking();
- method public final synchronized boolean isRegistered();
- method public final synchronized java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
+ method public final boolean isRegistered();
+ method public final java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
method public final java.nio.channels.spi.SelectorProvider provider();
method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, java.lang.Object) throws java.nio.channels.ClosedChannelException;
}
@@ -47246,15 +47730,25 @@
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
+ public abstract class AsynchronousChannelProvider {
+ ctor protected AsynchronousChannelProvider();
+ method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
+ }
+
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel openDatagramChannel() throws java.io.IOException;
+ method public abstract java.nio.channels.DatagramChannel openDatagramChannel(java.net.ProtocolFamily) throws java.io.IOException;
method public abstract java.nio.channels.Pipe openPipe() throws java.io.IOException;
method public abstract java.nio.channels.spi.AbstractSelector openSelector() throws java.io.IOException;
method public abstract java.nio.channels.ServerSocketChannel openServerSocketChannel() throws java.io.IOException;
method public abstract java.nio.channels.SocketChannel openSocketChannel() throws java.io.IOException;
- method public static synchronized java.nio.channels.spi.SelectorProvider provider();
+ method public static java.nio.channels.spi.SelectorProvider provider();
}
}
@@ -47293,8 +47787,8 @@
ctor protected CharsetDecoder(java.nio.charset.Charset, float, float);
method public final float averageCharsPerByte();
method public final java.nio.charset.Charset charset();
- method public final java.nio.CharBuffer decode(java.nio.ByteBuffer) throws java.nio.charset.CharacterCodingException;
method public final java.nio.charset.CoderResult decode(java.nio.ByteBuffer, java.nio.CharBuffer, boolean);
+ method public final java.nio.CharBuffer decode(java.nio.ByteBuffer) throws java.nio.charset.CharacterCodingException;
method protected abstract java.nio.charset.CoderResult decodeLoop(java.nio.ByteBuffer, java.nio.CharBuffer);
method public java.nio.charset.Charset detectedCharset();
method public final java.nio.charset.CoderResult flush(java.nio.CharBuffer);
@@ -47316,14 +47810,14 @@
}
public abstract class CharsetEncoder {
- ctor protected CharsetEncoder(java.nio.charset.Charset, float, float);
ctor protected CharsetEncoder(java.nio.charset.Charset, float, float, byte[]);
+ ctor protected CharsetEncoder(java.nio.charset.Charset, float, float);
method public final float averageBytesPerChar();
method public boolean canEncode(char);
method public boolean canEncode(java.lang.CharSequence);
method public final java.nio.charset.Charset charset();
- method public final java.nio.ByteBuffer encode(java.nio.CharBuffer) throws java.nio.charset.CharacterCodingException;
method public final java.nio.charset.CoderResult encode(java.nio.CharBuffer, java.nio.ByteBuffer, boolean);
+ method public final java.nio.ByteBuffer encode(java.nio.CharBuffer) throws java.nio.charset.CharacterCodingException;
method protected abstract java.nio.charset.CoderResult encodeLoop(java.nio.CharBuffer, java.nio.ByteBuffer);
method public final java.nio.charset.CoderResult flush(java.nio.ByteBuffer);
method protected java.nio.charset.CoderResult implFlush(java.nio.ByteBuffer);
@@ -47352,10 +47846,10 @@
method public boolean isOverflow();
method public boolean isUnderflow();
method public boolean isUnmappable();
- method public int length() throws java.lang.UnsupportedOperationException;
- method public static synchronized java.nio.charset.CoderResult malformedForLength(int) throws java.lang.IllegalArgumentException;
- method public void throwException() throws java.nio.BufferOverflowException, java.nio.BufferUnderflowException, java.nio.charset.CharacterCodingException, java.nio.charset.MalformedInputException, java.nio.charset.UnmappableCharacterException;
- method public static synchronized java.nio.charset.CoderResult unmappableForLength(int) throws java.lang.IllegalArgumentException;
+ method public int length();
+ method public static java.nio.charset.CoderResult malformedForLength(int);
+ method public void throwException() throws java.nio.charset.CharacterCodingException;
+ method public static java.nio.charset.CoderResult unmappableForLength(int);
field public static final java.nio.charset.CoderResult OVERFLOW;
field public static final java.nio.charset.CoderResult UNDERFLOW;
}
@@ -47407,11 +47901,597 @@
}
+package java.nio.file {
+
+ public class AccessDeniedException extends java.nio.file.FileSystemException {
+ ctor public AccessDeniedException(java.lang.String);
+ ctor public AccessDeniedException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public final class AccessMode extends java.lang.Enum {
+ method public static java.nio.file.AccessMode valueOf(java.lang.String);
+ method public static final java.nio.file.AccessMode[] values();
+ enum_constant public static final java.nio.file.AccessMode EXECUTE;
+ enum_constant public static final java.nio.file.AccessMode READ;
+ enum_constant public static final java.nio.file.AccessMode WRITE;
+ }
+
+ public class AtomicMoveNotSupportedException extends java.nio.file.FileSystemException {
+ ctor public AtomicMoveNotSupportedException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public class ClosedDirectoryStreamException extends java.lang.IllegalStateException {
+ ctor public ClosedDirectoryStreamException();
+ }
+
+ public class ClosedFileSystemException extends java.lang.IllegalStateException {
+ ctor public ClosedFileSystemException();
+ }
+
+ public class ClosedWatchServiceException extends java.lang.IllegalStateException {
+ ctor public ClosedWatchServiceException();
+ }
+
+ public abstract interface CopyOption {
+ }
+
+ public final class DirectoryIteratorException extends java.util.ConcurrentModificationException {
+ ctor public DirectoryIteratorException(java.io.IOException);
+ }
+
+ public class DirectoryNotEmptyException extends java.nio.file.FileSystemException {
+ ctor public DirectoryNotEmptyException(java.lang.String);
+ }
+
+ public abstract interface DirectoryStream implements java.io.Closeable java.lang.Iterable {
+ method public abstract java.util.Iterator<T> iterator();
+ }
+
+ public static abstract interface DirectoryStream.Filter {
+ method public abstract boolean accept(T) throws java.io.IOException;
+ }
+
+ public class FileAlreadyExistsException extends java.nio.file.FileSystemException {
+ ctor public FileAlreadyExistsException(java.lang.String);
+ ctor public FileAlreadyExistsException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract class FileStore {
+ ctor protected FileStore();
+ method public abstract java.lang.Object getAttribute(java.lang.String) throws java.io.IOException;
+ method public abstract V getFileStoreAttributeView(java.lang.Class<V>);
+ method public abstract long getTotalSpace() throws java.io.IOException;
+ method public abstract long getUnallocatedSpace() throws java.io.IOException;
+ method public abstract long getUsableSpace() throws java.io.IOException;
+ method public abstract boolean isReadOnly();
+ method public abstract java.lang.String name();
+ method public abstract boolean supportsFileAttributeView(java.lang.Class<? extends java.nio.file.attribute.FileAttributeView>);
+ method public abstract boolean supportsFileAttributeView(java.lang.String);
+ method public abstract java.lang.String type();
+ }
+
+ public abstract class FileSystem implements java.io.Closeable {
+ ctor protected FileSystem();
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.lang.Iterable<java.nio.file.FileStore> getFileStores();
+ method public abstract java.nio.file.Path getPath(java.lang.String, java.lang.String...);
+ method public abstract java.nio.file.PathMatcher getPathMatcher(java.lang.String);
+ method public abstract java.lang.Iterable<java.nio.file.Path> getRootDirectories();
+ method public abstract java.lang.String getSeparator();
+ method public abstract java.nio.file.attribute.UserPrincipalLookupService getUserPrincipalLookupService();
+ method public abstract boolean isOpen();
+ method public abstract boolean isReadOnly();
+ method public abstract java.nio.file.WatchService newWatchService() throws java.io.IOException;
+ method public abstract java.nio.file.spi.FileSystemProvider provider();
+ method public abstract java.util.Set<java.lang.String> supportedFileAttributeViews();
+ }
+
+ public class FileSystemAlreadyExistsException extends java.lang.RuntimeException {
+ ctor public FileSystemAlreadyExistsException();
+ ctor public FileSystemAlreadyExistsException(java.lang.String);
+ }
+
+ public class FileSystemException extends java.io.IOException {
+ ctor public FileSystemException(java.lang.String);
+ ctor public FileSystemException(java.lang.String, java.lang.String, java.lang.String);
+ method public java.lang.String getFile();
+ method public java.lang.String getOtherFile();
+ method public java.lang.String getReason();
+ }
+
+ public class FileSystemLoopException extends java.nio.file.FileSystemException {
+ ctor public FileSystemLoopException(java.lang.String);
+ }
+
+ public class FileSystemNotFoundException extends java.lang.RuntimeException {
+ ctor public FileSystemNotFoundException();
+ ctor public FileSystemNotFoundException(java.lang.String);
+ }
+
+ public final class FileSystems {
+ method public static java.nio.file.FileSystem getDefault();
+ method public static java.nio.file.FileSystem getFileSystem(java.net.URI);
+ method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>, java.lang.ClassLoader) throws java.io.IOException;
+ method public static java.nio.file.FileSystem newFileSystem(java.nio.file.Path, java.lang.ClassLoader) throws java.io.IOException;
+ }
+
+ public final class FileVisitOption extends java.lang.Enum {
+ method public static java.nio.file.FileVisitOption valueOf(java.lang.String);
+ method public static final java.nio.file.FileVisitOption[] values();
+ enum_constant public static final java.nio.file.FileVisitOption FOLLOW_LINKS;
+ }
+
+ public final class FileVisitResult extends java.lang.Enum {
+ method public static java.nio.file.FileVisitResult valueOf(java.lang.String);
+ method public static final java.nio.file.FileVisitResult[] values();
+ enum_constant public static final java.nio.file.FileVisitResult CONTINUE;
+ enum_constant public static final java.nio.file.FileVisitResult SKIP_SIBLINGS;
+ enum_constant public static final java.nio.file.FileVisitResult SKIP_SUBTREE;
+ enum_constant public static final java.nio.file.FileVisitResult TERMINATE;
+ }
+
+ public abstract interface FileVisitor {
+ method public abstract java.nio.file.FileVisitResult postVisitDirectory(T, java.io.IOException) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult preVisitDirectory(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult visitFile(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult visitFileFailed(T, java.io.IOException) throws java.io.IOException;
+ }
+
+ public final class Files {
+ method public static java.nio.file.Path copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static long copy(java.io.InputStream, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static long copy(java.nio.file.Path, java.io.OutputStream) throws java.io.IOException;
+ method public static java.nio.file.Path createDirectories(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createFile(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempDirectory(java.nio.file.Path, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempDirectory(java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempFile(java.nio.file.Path, java.lang.String, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempFile(java.lang.String, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static void delete(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean exists(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static java.lang.Object getAttribute(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static V getFileAttributeView(java.nio.file.Path, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public static java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.attribute.FileTime getLastModifiedTime(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.attribute.UserPrincipal getOwner(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> getPosixFilePermissions(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static boolean isDirectory(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static boolean isExecutable(java.nio.file.Path);
+ method public static boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean isReadable(java.nio.file.Path);
+ method public static boolean isRegularFile(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public static boolean isSymbolicLink(java.nio.file.Path);
+ method public static boolean isWritable(java.nio.file.Path);
+ method public static java.nio.file.Path move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static java.io.BufferedReader newBufferedReader(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+ method public static java.io.BufferedWriter newBufferedWriter(java.nio.file.Path, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.lang.String) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static boolean notExists(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static java.lang.String probeContentType(java.nio.file.Path) throws java.io.IOException;
+ method public static byte[] readAllBytes(java.nio.file.Path) throws java.io.IOException;
+ method public static java.util.List<java.lang.String> readAllLines(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+ method public static A readAttributes(java.nio.file.Path, java.lang.Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.util.Map<java.lang.String, java.lang.Object> readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.Path setLastModifiedTime(java.nio.file.Path, java.nio.file.attribute.FileTime) throws java.io.IOException;
+ method public static java.nio.file.Path setOwner(java.nio.file.Path, java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+ method public static java.nio.file.Path setPosixFilePermissions(java.nio.file.Path, java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+ method public static long size(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.util.Set<java.nio.file.FileVisitOption>, int, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.nio.file.Path write(java.nio.file.Path, byte[], java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.file.Path write(java.nio.file.Path, java.lang.Iterable<? extends java.lang.CharSequence>, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+ }
+
+ public class InvalidPathException extends java.lang.IllegalArgumentException {
+ ctor public InvalidPathException(java.lang.String, java.lang.String, int);
+ ctor public InvalidPathException(java.lang.String, java.lang.String);
+ method public int getIndex();
+ method public java.lang.String getInput();
+ method public java.lang.String getReason();
+ }
+
+ public final class LinkOption extends java.lang.Enum implements java.nio.file.CopyOption java.nio.file.OpenOption {
+ method public static java.nio.file.LinkOption valueOf(java.lang.String);
+ method public static final java.nio.file.LinkOption[] values();
+ enum_constant public static final java.nio.file.LinkOption NOFOLLOW_LINKS;
+ }
+
+ public final class LinkPermission extends java.security.BasicPermission {
+ ctor public LinkPermission(java.lang.String);
+ ctor public LinkPermission(java.lang.String, java.lang.String);
+ }
+
+ public class NoSuchFileException extends java.nio.file.FileSystemException {
+ ctor public NoSuchFileException(java.lang.String);
+ ctor public NoSuchFileException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public class NotDirectoryException extends java.nio.file.FileSystemException {
+ ctor public NotDirectoryException(java.lang.String);
+ }
+
+ public class NotLinkException extends java.nio.file.FileSystemException {
+ ctor public NotLinkException(java.lang.String);
+ ctor public NotLinkException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract interface OpenOption {
+ }
+
+ public abstract interface Path implements java.lang.Comparable java.lang.Iterable java.nio.file.Watchable {
+ method public abstract int compareTo(java.nio.file.Path);
+ method public abstract boolean endsWith(java.nio.file.Path);
+ method public abstract boolean endsWith(java.lang.String);
+ method public abstract boolean equals(java.lang.Object);
+ method public abstract java.nio.file.Path getFileName();
+ method public abstract java.nio.file.FileSystem getFileSystem();
+ method public abstract java.nio.file.Path getName(int);
+ method public abstract int getNameCount();
+ method public abstract java.nio.file.Path getParent();
+ method public abstract java.nio.file.Path getRoot();
+ method public abstract int hashCode();
+ method public abstract boolean isAbsolute();
+ method public abstract java.util.Iterator<java.nio.file.Path> iterator();
+ method public abstract java.nio.file.Path normalize();
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>[], java.nio.file.WatchEvent.Modifier...) throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.Path relativize(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolve(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolve(java.lang.String);
+ method public abstract java.nio.file.Path resolveSibling(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolveSibling(java.lang.String);
+ method public abstract boolean startsWith(java.nio.file.Path);
+ method public abstract boolean startsWith(java.lang.String);
+ method public abstract java.nio.file.Path subpath(int, int);
+ method public abstract java.nio.file.Path toAbsolutePath();
+ method public abstract java.io.File toFile();
+ method public abstract java.nio.file.Path toRealPath(java.nio.file.LinkOption...) throws java.io.IOException;
+ method public abstract java.lang.String toString();
+ method public abstract java.net.URI toUri();
+ }
+
+ public abstract interface PathMatcher {
+ method public abstract boolean matches(java.nio.file.Path);
+ }
+
+ public final class Paths {
+ method public static java.nio.file.Path get(java.lang.String, java.lang.String...);
+ method public static java.nio.file.Path get(java.net.URI);
+ }
+
+ public class ProviderMismatchException extends java.lang.IllegalArgumentException {
+ ctor public ProviderMismatchException();
+ ctor public ProviderMismatchException(java.lang.String);
+ }
+
+ public class ProviderNotFoundException extends java.lang.RuntimeException {
+ ctor public ProviderNotFoundException();
+ ctor public ProviderNotFoundException(java.lang.String);
+ }
+
+ public class ReadOnlyFileSystemException extends java.lang.UnsupportedOperationException {
+ ctor public ReadOnlyFileSystemException();
+ }
+
+ public abstract interface SecureDirectoryStream implements java.nio.file.DirectoryStream {
+ method public abstract void deleteDirectory(T) throws java.io.IOException;
+ method public abstract void deleteFile(T) throws java.io.IOException;
+ method public abstract V getFileAttributeView(java.lang.Class<V>);
+ method public abstract V getFileAttributeView(T, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public abstract void move(T, java.nio.file.SecureDirectoryStream<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel newByteChannel(T, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.SecureDirectoryStream<T> newDirectoryStream(T, java.nio.file.LinkOption...) throws java.io.IOException;
+ }
+
+ public final class StandardCopyOption extends java.lang.Enum implements java.nio.file.CopyOption {
+ method public static java.nio.file.StandardCopyOption valueOf(java.lang.String);
+ method public static final java.nio.file.StandardCopyOption[] values();
+ enum_constant public static final java.nio.file.StandardCopyOption ATOMIC_MOVE;
+ enum_constant public static final java.nio.file.StandardCopyOption COPY_ATTRIBUTES;
+ enum_constant public static final java.nio.file.StandardCopyOption REPLACE_EXISTING;
+ }
+
+ public final class StandardOpenOption extends java.lang.Enum implements java.nio.file.OpenOption {
+ method public static java.nio.file.StandardOpenOption valueOf(java.lang.String);
+ method public static final java.nio.file.StandardOpenOption[] values();
+ enum_constant public static final java.nio.file.StandardOpenOption APPEND;
+ enum_constant public static final java.nio.file.StandardOpenOption CREATE;
+ enum_constant public static final java.nio.file.StandardOpenOption CREATE_NEW;
+ enum_constant public static final java.nio.file.StandardOpenOption DELETE_ON_CLOSE;
+ enum_constant public static final java.nio.file.StandardOpenOption DSYNC;
+ enum_constant public static final java.nio.file.StandardOpenOption READ;
+ enum_constant public static final java.nio.file.StandardOpenOption SPARSE;
+ enum_constant public static final java.nio.file.StandardOpenOption SYNC;
+ enum_constant public static final java.nio.file.StandardOpenOption TRUNCATE_EXISTING;
+ enum_constant public static final java.nio.file.StandardOpenOption WRITE;
+ }
+
+ public final class StandardWatchEventKinds {
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_CREATE;
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_DELETE;
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_MODIFY;
+ field public static final java.nio.file.WatchEvent.Kind<java.lang.Object> OVERFLOW;
+ }
+
+ public abstract interface WatchEvent {
+ method public abstract T context();
+ method public abstract int count();
+ method public abstract java.nio.file.WatchEvent.Kind<T> kind();
+ }
+
+ public static abstract interface WatchEvent.Kind {
+ method public abstract java.lang.String name();
+ method public abstract java.lang.Class<T> type();
+ }
+
+ public static abstract interface WatchEvent.Modifier {
+ method public abstract java.lang.String name();
+ }
+
+ public abstract interface WatchKey {
+ method public abstract void cancel();
+ method public abstract boolean isValid();
+ method public abstract java.util.List<java.nio.file.WatchEvent<?>> pollEvents();
+ method public abstract boolean reset();
+ method public abstract java.nio.file.Watchable watchable();
+ }
+
+ public abstract interface WatchService implements java.io.Closeable {
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey poll();
+ method public abstract java.nio.file.WatchKey poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public abstract java.nio.file.WatchKey take() throws java.lang.InterruptedException;
+ }
+
+ public abstract interface Watchable {
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>[], java.nio.file.WatchEvent.Modifier...) throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>...) throws java.io.IOException;
+ }
+
+}
+
+package java.nio.file.attribute {
+
+ public final class AclEntry {
+ method public java.util.Set<java.nio.file.attribute.AclEntryFlag> flags();
+ method public static java.nio.file.attribute.AclEntry.Builder newBuilder();
+ method public static java.nio.file.attribute.AclEntry.Builder newBuilder(java.nio.file.attribute.AclEntry);
+ method public java.util.Set<java.nio.file.attribute.AclEntryPermission> permissions();
+ method public java.nio.file.attribute.UserPrincipal principal();
+ method public java.nio.file.attribute.AclEntryType type();
+ }
+
+ public static final class AclEntry.Builder {
+ method public java.nio.file.attribute.AclEntry build();
+ method public java.nio.file.attribute.AclEntry.Builder setFlags(java.util.Set<java.nio.file.attribute.AclEntryFlag>);
+ method public java.nio.file.attribute.AclEntry.Builder setFlags(java.nio.file.attribute.AclEntryFlag...);
+ method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.util.Set<java.nio.file.attribute.AclEntryPermission>);
+ method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.nio.file.attribute.AclEntryPermission...);
+ method public java.nio.file.attribute.AclEntry.Builder setPrincipal(java.nio.file.attribute.UserPrincipal);
+ method public java.nio.file.attribute.AclEntry.Builder setType(java.nio.file.attribute.AclEntryType);
+ }
+
+ public final class AclEntryFlag extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryFlag valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryFlag[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag DIRECTORY_INHERIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag FILE_INHERIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag INHERIT_ONLY;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag NO_PROPAGATE_INHERIT;
+ }
+
+ public final class AclEntryPermission extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryPermission valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryPermission[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission APPEND_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE_CHILD;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission EXECUTE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ACL;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ATTRIBUTES;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_NAMED_ATTRS;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission SYNCHRONIZE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ACL;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ATTRIBUTES;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_NAMED_ATTRS;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_OWNER;
+ field public static final java.nio.file.attribute.AclEntryPermission ADD_FILE;
+ field public static final java.nio.file.attribute.AclEntryPermission ADD_SUBDIRECTORY;
+ field public static final java.nio.file.attribute.AclEntryPermission LIST_DIRECTORY;
+ }
+
+ public final class AclEntryType extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryType valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryType[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryType ALARM;
+ enum_constant public static final java.nio.file.attribute.AclEntryType ALLOW;
+ enum_constant public static final java.nio.file.attribute.AclEntryType AUDIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryType DENY;
+ }
+
+ public abstract interface AclFileAttributeView implements java.nio.file.attribute.FileOwnerAttributeView {
+ method public abstract java.util.List<java.nio.file.attribute.AclEntry> getAcl() throws java.io.IOException;
+ method public abstract java.lang.String name();
+ method public abstract void setAcl(java.util.List<java.nio.file.attribute.AclEntry>) throws java.io.IOException;
+ }
+
+ public abstract interface AttributeView {
+ method public abstract java.lang.String name();
+ }
+
+ public abstract interface BasicFileAttributeView implements java.nio.file.attribute.FileAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.BasicFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setTimes(java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime) throws java.io.IOException;
+ }
+
+ public abstract interface BasicFileAttributes {
+ method public abstract java.nio.file.attribute.FileTime creationTime();
+ method public abstract java.lang.Object fileKey();
+ method public abstract boolean isDirectory();
+ method public abstract boolean isOther();
+ method public abstract boolean isRegularFile();
+ method public abstract boolean isSymbolicLink();
+ method public abstract java.nio.file.attribute.FileTime lastAccessTime();
+ method public abstract java.nio.file.attribute.FileTime lastModifiedTime();
+ method public abstract long size();
+ }
+
+ public abstract interface DosFileAttributeView implements java.nio.file.attribute.BasicFileAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.DosFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setArchive(boolean) throws java.io.IOException;
+ method public abstract void setHidden(boolean) throws java.io.IOException;
+ method public abstract void setReadOnly(boolean) throws java.io.IOException;
+ method public abstract void setSystem(boolean) throws java.io.IOException;
+ }
+
+ public abstract interface DosFileAttributes implements java.nio.file.attribute.BasicFileAttributes {
+ method public abstract boolean isArchive();
+ method public abstract boolean isHidden();
+ method public abstract boolean isReadOnly();
+ method public abstract boolean isSystem();
+ }
+
+ public abstract interface FileAttribute {
+ method public abstract java.lang.String name();
+ method public abstract T value();
+ }
+
+ public abstract interface FileAttributeView implements java.nio.file.attribute.AttributeView {
+ }
+
+ public abstract interface FileOwnerAttributeView implements java.nio.file.attribute.FileAttributeView {
+ method public abstract java.nio.file.attribute.UserPrincipal getOwner() throws java.io.IOException;
+ method public abstract java.lang.String name();
+ method public abstract void setOwner(java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+ }
+
+ public abstract interface FileStoreAttributeView implements java.nio.file.attribute.AttributeView {
+ }
+
+ public final class FileTime implements java.lang.Comparable {
+ method public int compareTo(java.nio.file.attribute.FileTime);
+ method public static java.nio.file.attribute.FileTime from(long, java.util.concurrent.TimeUnit);
+ method public static java.nio.file.attribute.FileTime fromMillis(long);
+ method public long to(java.util.concurrent.TimeUnit);
+ method public long toMillis();
+ }
+
+ public abstract interface GroupPrincipal implements java.nio.file.attribute.UserPrincipal {
+ }
+
+ public abstract interface PosixFileAttributeView implements java.nio.file.attribute.BasicFileAttributeView java.nio.file.attribute.FileOwnerAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.PosixFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setGroup(java.nio.file.attribute.GroupPrincipal) throws java.io.IOException;
+ method public abstract void setPermissions(java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+ }
+
+ public abstract interface PosixFileAttributes implements java.nio.file.attribute.BasicFileAttributes {
+ method public abstract java.nio.file.attribute.GroupPrincipal group();
+ method public abstract java.nio.file.attribute.UserPrincipal owner();
+ method public abstract java.util.Set<java.nio.file.attribute.PosixFilePermission> permissions();
+ }
+
+ public final class PosixFilePermission extends java.lang.Enum {
+ method public static java.nio.file.attribute.PosixFilePermission valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.PosixFilePermission[] values();
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_WRITE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_WRITE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_WRITE;
+ }
+
+ public final class PosixFilePermissions {
+ method public static java.nio.file.attribute.FileAttribute<java.util.Set<java.nio.file.attribute.PosixFilePermission>> asFileAttribute(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+ method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> fromString(java.lang.String);
+ method public static java.lang.String toString(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+ }
+
+ public abstract interface UserPrincipal implements java.security.Principal {
+ }
+
+ public abstract class UserPrincipalLookupService {
+ ctor protected UserPrincipalLookupService();
+ method public abstract java.nio.file.attribute.GroupPrincipal lookupPrincipalByGroupName(java.lang.String) throws java.io.IOException;
+ method public abstract java.nio.file.attribute.UserPrincipal lookupPrincipalByName(java.lang.String) throws java.io.IOException;
+ }
+
+ public class UserPrincipalNotFoundException extends java.io.IOException {
+ ctor public UserPrincipalNotFoundException(java.lang.String);
+ method public java.lang.String getName();
+ }
+
+}
+
+package java.nio.file.spi {
+
+ public abstract class FileSystemProvider {
+ ctor protected FileSystemProvider();
+ method public abstract void checkAccess(java.nio.file.Path, java.nio.file.AccessMode...) throws java.io.IOException;
+ method public abstract void copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public abstract void createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public void createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public void createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract void delete(java.nio.file.Path) throws java.io.IOException;
+ method public boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+ method public abstract V getFileAttributeView(java.nio.file.Path, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public abstract java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+ method public abstract java.nio.file.FileSystem getFileSystem(java.net.URI);
+ method public abstract java.nio.file.Path getPath(java.net.URI);
+ method public abstract java.lang.String getScheme();
+ method public static java.util.List<java.nio.file.spi.FileSystemProvider> installedProviders();
+ method public abstract boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+ method public abstract boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public abstract void move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public java.nio.channels.AsynchronousFileChannel newAsynchronousFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+ method public java.nio.channels.FileChannel newFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public java.nio.file.FileSystem newFileSystem(java.nio.file.Path, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public abstract A readAttributes(java.nio.file.Path, java.lang.Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public abstract java.util.Map<java.lang.String, java.lang.Object> readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+ method public abstract void setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption...) throws java.io.IOException;
+ }
+
+ public abstract class FileTypeDetector {
+ ctor protected FileTypeDetector();
+ method public abstract java.lang.String probeContentType(java.nio.file.Path) throws java.io.IOException;
+ }
+
+}
+
package java.security {
public final class AccessControlContext {
- ctor public AccessControlContext(java.security.AccessControlContext, java.security.DomainCombiner);
ctor public AccessControlContext(java.security.ProtectionDomain[]);
+ ctor public AccessControlContext(java.security.AccessControlContext, java.security.DomainCombiner);
method public void checkPermission(java.security.Permission) throws java.security.AccessControlException;
method public java.security.DomainCombiner getDomainCombiner();
}
@@ -47433,6 +48513,12 @@
method public static java.security.AccessControlContext getContext();
}
+ public abstract interface AlgorithmConstraints {
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.lang.String, java.security.AlgorithmParameters);
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.security.Key);
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.lang.String, java.security.Key, java.security.AlgorithmParameters);
+ }
+
public class AlgorithmParameterGenerator {
ctor protected AlgorithmParameterGenerator(java.security.AlgorithmParameterGeneratorSpi, java.security.Provider, java.lang.String);
method public final java.security.AlgorithmParameters generateParameters();
@@ -47482,9 +48568,11 @@
}
public final class AllPermission extends java.security.Permission {
- ctor public AllPermission(java.lang.String, java.lang.String);
ctor public AllPermission();
+ ctor public AllPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -47498,7 +48586,9 @@
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -47527,9 +48617,24 @@
method public boolean implies(java.security.CodeSource);
}
+ public final class CryptoPrimitive extends java.lang.Enum {
+ method public static java.security.CryptoPrimitive valueOf(java.lang.String);
+ method public static final java.security.CryptoPrimitive[] values();
+ enum_constant public static final java.security.CryptoPrimitive BLOCK_CIPHER;
+ enum_constant public static final java.security.CryptoPrimitive KEY_AGREEMENT;
+ enum_constant public static final java.security.CryptoPrimitive KEY_ENCAPSULATION;
+ enum_constant public static final java.security.CryptoPrimitive KEY_WRAP;
+ enum_constant public static final java.security.CryptoPrimitive MAC;
+ enum_constant public static final java.security.CryptoPrimitive MESSAGE_DIGEST;
+ enum_constant public static final java.security.CryptoPrimitive PUBLIC_KEY_ENCRYPTION;
+ enum_constant public static final java.security.CryptoPrimitive SECURE_RANDOM;
+ enum_constant public static final java.security.CryptoPrimitive SIGNATURE;
+ enum_constant public static final java.security.CryptoPrimitive STREAM_CIPHER;
+ }
+
public class DigestException extends java.security.GeneralSecurityException {
- ctor public DigestException(java.lang.String);
ctor public DigestException();
+ ctor public DigestException(java.lang.String);
ctor public DigestException(java.lang.String, java.lang.Throwable);
ctor public DigestException(java.lang.Throwable);
}
@@ -47555,8 +48660,8 @@
}
public class GeneralSecurityException extends java.lang.Exception {
- ctor public GeneralSecurityException(java.lang.String);
ctor public GeneralSecurityException();
+ ctor public GeneralSecurityException(java.lang.String);
ctor public GeneralSecurityException(java.lang.String, java.lang.Throwable);
ctor public GeneralSecurityException(java.lang.Throwable);
}
@@ -47572,8 +48677,8 @@
public abstract deprecated class Identity implements java.security.Principal java.io.Serializable {
ctor protected Identity();
- ctor public Identity(java.lang.String);
ctor public Identity(java.lang.String, java.security.IdentityScope) throws java.security.KeyManagementException;
+ ctor public Identity(java.lang.String);
method public void addCertificate(java.security.Certificate) throws java.security.KeyManagementException;
method public java.security.Certificate[] certificates();
method public final boolean equals(java.lang.Object);
@@ -47604,22 +48709,22 @@
}
public class InvalidAlgorithmParameterException extends java.security.GeneralSecurityException {
- ctor public InvalidAlgorithmParameterException(java.lang.String);
ctor public InvalidAlgorithmParameterException();
+ ctor public InvalidAlgorithmParameterException(java.lang.String);
ctor public InvalidAlgorithmParameterException(java.lang.String, java.lang.Throwable);
ctor public InvalidAlgorithmParameterException(java.lang.Throwable);
}
public class InvalidKeyException extends java.security.KeyException {
- ctor public InvalidKeyException(java.lang.String);
ctor public InvalidKeyException();
+ ctor public InvalidKeyException(java.lang.String);
ctor public InvalidKeyException(java.lang.String, java.lang.Throwable);
ctor public InvalidKeyException(java.lang.Throwable);
}
public class InvalidParameterException extends java.lang.IllegalArgumentException {
- ctor public InvalidParameterException(java.lang.String);
ctor public InvalidParameterException();
+ ctor public InvalidParameterException(java.lang.String);
}
public abstract interface Key implements java.io.Serializable {
@@ -47630,8 +48735,8 @@
}
public class KeyException extends java.security.GeneralSecurityException {
- ctor public KeyException(java.lang.String);
ctor public KeyException();
+ ctor public KeyException(java.lang.String);
ctor public KeyException(java.lang.String, java.lang.Throwable);
ctor public KeyException(java.lang.Throwable);
}
@@ -47658,8 +48763,8 @@
}
public class KeyManagementException extends java.security.KeyException {
- ctor public KeyManagementException(java.lang.String);
ctor public KeyManagementException();
+ ctor public KeyManagementException(java.lang.String);
ctor public KeyManagementException(java.lang.String, java.lang.Throwable);
ctor public KeyManagementException(java.lang.Throwable);
}
@@ -47680,8 +48785,8 @@
method public static java.security.KeyPairGenerator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
method public void initialize(int);
- method public void initialize(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
method public void initialize(int, java.security.SecureRandom);
+ method public void initialize(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
}
public abstract class KeyPairGeneratorSpi {
@@ -47784,8 +48889,8 @@
}
public class KeyStoreException extends java.security.GeneralSecurityException {
- ctor public KeyStoreException(java.lang.String);
ctor public KeyStoreException();
+ ctor public KeyStoreException(java.lang.String);
ctor public KeyStoreException(java.lang.String, java.lang.Throwable);
ctor public KeyStoreException(java.lang.Throwable);
}
@@ -47847,22 +48952,24 @@
}
public class NoSuchAlgorithmException extends java.security.GeneralSecurityException {
- ctor public NoSuchAlgorithmException(java.lang.String);
ctor public NoSuchAlgorithmException();
+ ctor public NoSuchAlgorithmException(java.lang.String);
ctor public NoSuchAlgorithmException(java.lang.String, java.lang.Throwable);
ctor public NoSuchAlgorithmException(java.lang.Throwable);
}
public class NoSuchProviderException extends java.security.GeneralSecurityException {
- ctor public NoSuchProviderException(java.lang.String);
ctor public NoSuchProviderException();
+ ctor public NoSuchProviderException(java.lang.String);
}
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
+ method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
+ method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -47970,8 +49077,8 @@
}
public class ProviderException extends java.lang.RuntimeException {
- ctor public ProviderException(java.lang.String);
ctor public ProviderException();
+ ctor public ProviderException(java.lang.String);
ctor public ProviderException(java.lang.String, java.lang.Throwable);
ctor public ProviderException(java.lang.Throwable);
}
@@ -47981,8 +49088,8 @@
}
public class SecureClassLoader extends java.lang.ClassLoader {
- ctor protected SecureClassLoader();
ctor protected SecureClassLoader(java.lang.ClassLoader);
+ ctor protected SecureClassLoader();
method protected final java.lang.Class<?> defineClass(java.lang.String, byte[], int, int, java.security.CodeSource);
method protected final java.lang.Class<?> defineClass(java.lang.String, java.nio.ByteBuffer, java.security.CodeSource);
method protected java.security.PermissionCollection getPermissions(java.security.CodeSource);
@@ -48015,10 +49122,10 @@
method public static deprecated java.lang.String getAlgorithmProperty(java.lang.String, java.lang.String);
method public static java.util.Set<java.lang.String> getAlgorithms(java.lang.String);
method public static java.lang.String getProperty(java.lang.String);
- method public static synchronized java.security.Provider getProvider(java.lang.String);
- method public static synchronized java.security.Provider[] getProviders();
+ method public static java.security.Provider getProvider(java.lang.String);
+ method public static java.security.Provider[] getProviders();
method public static java.security.Provider[] getProviders(java.lang.String);
- method public static synchronized java.security.Provider[] getProviders(java.util.Map<java.lang.String, java.lang.String>);
+ method public static java.security.Provider[] getProviders(java.util.Map<java.lang.String, java.lang.String>);
method public static synchronized int insertProviderAt(java.security.Provider, int);
method public static synchronized void removeProvider(java.lang.String);
method public static void setProperty(java.lang.String, java.lang.String);
@@ -48032,6 +49139,7 @@
public abstract class Signature extends java.security.SignatureSpi {
ctor protected Signature(java.lang.String);
method public final java.lang.String getAlgorithm();
+ method public java.security.SignatureSpi getCurrentSpi();
method public static java.security.Signature getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
method public static java.security.Signature getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.Signature getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
@@ -48059,8 +49167,8 @@
}
public class SignatureException extends java.security.GeneralSecurityException {
- ctor public SignatureException(java.lang.String);
ctor public SignatureException();
+ ctor public SignatureException(java.lang.String);
ctor public SignatureException(java.lang.String, java.lang.Throwable);
ctor public SignatureException(java.lang.Throwable);
}
@@ -48113,17 +49221,19 @@
}
public class UnrecoverableKeyException extends java.security.UnrecoverableEntryException {
- ctor public UnrecoverableKeyException(java.lang.String);
ctor public UnrecoverableKeyException();
+ ctor public UnrecoverableKeyException(java.lang.String);
}
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -48197,12 +49307,28 @@
}
public class CRLException extends java.security.GeneralSecurityException {
- ctor public CRLException(java.lang.String);
ctor public CRLException();
+ ctor public CRLException(java.lang.String);
ctor public CRLException(java.lang.String, java.lang.Throwable);
ctor public CRLException(java.lang.Throwable);
}
+ public final class CRLReason extends java.lang.Enum {
+ method public static java.security.cert.CRLReason valueOf(java.lang.String);
+ method public static final java.security.cert.CRLReason[] values();
+ enum_constant public static final java.security.cert.CRLReason AA_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason AFFILIATION_CHANGED;
+ enum_constant public static final java.security.cert.CRLReason CA_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason CERTIFICATE_HOLD;
+ enum_constant public static final java.security.cert.CRLReason CESSATION_OF_OPERATION;
+ enum_constant public static final java.security.cert.CRLReason KEY_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason PRIVILEGE_WITHDRAWN;
+ enum_constant public static final java.security.cert.CRLReason REMOVE_FROM_CRL;
+ enum_constant public static final java.security.cert.CRLReason SUPERSEDED;
+ enum_constant public static final java.security.cert.CRLReason UNSPECIFIED;
+ enum_constant public static final java.security.cert.CRLReason UNUSED;
+ }
+
public abstract interface CRLSelector implements java.lang.Cloneable {
method public abstract java.lang.Object clone();
method public abstract boolean match(java.security.cert.CRL);
@@ -48235,10 +49361,10 @@
}
public class CertPathBuilderException extends java.security.GeneralSecurityException {
- ctor public CertPathBuilderException(java.lang.String, java.lang.Throwable);
- ctor public CertPathBuilderException(java.lang.Throwable);
- ctor public CertPathBuilderException(java.lang.String);
ctor public CertPathBuilderException();
+ ctor public CertPathBuilderException(java.lang.String);
+ ctor public CertPathBuilderException(java.lang.Throwable);
+ ctor public CertPathBuilderException(java.lang.String, java.lang.Throwable);
}
public abstract interface CertPathBuilderResult implements java.lang.Cloneable {
@@ -48267,13 +49393,30 @@
}
public class CertPathValidatorException extends java.security.GeneralSecurityException {
- ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int);
- ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable);
- ctor public CertPathValidatorException(java.lang.Throwable);
- ctor public CertPathValidatorException(java.lang.String);
ctor public CertPathValidatorException();
+ ctor public CertPathValidatorException(java.lang.String);
+ ctor public CertPathValidatorException(java.lang.Throwable);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int, java.security.cert.CertPathValidatorException.Reason);
method public java.security.cert.CertPath getCertPath();
method public int getIndex();
+ method public java.security.cert.CertPathValidatorException.Reason getReason();
+ }
+
+ public static final class CertPathValidatorException.BasicReason extends java.lang.Enum implements java.security.cert.CertPathValidatorException.Reason {
+ method public static java.security.cert.CertPathValidatorException.BasicReason valueOf(java.lang.String);
+ method public static final java.security.cert.CertPathValidatorException.BasicReason[] values();
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason ALGORITHM_CONSTRAINED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason EXPIRED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason INVALID_SIGNATURE;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason NOT_YET_VALID;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason REVOKED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNDETERMINED_REVOCATION_STATUS;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNSPECIFIED;
+ }
+
+ public static abstract interface CertPathValidatorException.Reason implements java.io.Serializable {
}
public abstract interface CertPathValidatorResult implements java.lang.Cloneable {
@@ -48304,10 +49447,10 @@
}
public class CertStoreException extends java.security.GeneralSecurityException {
- ctor public CertStoreException(java.lang.String, java.lang.Throwable);
- ctor public CertStoreException(java.lang.Throwable);
- ctor public CertStoreException(java.lang.String);
ctor public CertStoreException();
+ ctor public CertStoreException(java.lang.String);
+ ctor public CertStoreException(java.lang.Throwable);
+ ctor public CertStoreException(java.lang.String, java.lang.Throwable);
}
public abstract interface CertStoreParameters implements java.lang.Cloneable {
@@ -48337,22 +49480,22 @@
}
public class CertificateEncodingException extends java.security.cert.CertificateException {
- ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException();
+ ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException(java.lang.String, java.lang.Throwable);
ctor public CertificateEncodingException(java.lang.Throwable);
}
public class CertificateException extends java.security.GeneralSecurityException {
- ctor public CertificateException(java.lang.String);
ctor public CertificateException();
+ ctor public CertificateException(java.lang.String);
ctor public CertificateException(java.lang.String, java.lang.Throwable);
ctor public CertificateException(java.lang.Throwable);
}
public class CertificateExpiredException extends java.security.cert.CertificateException {
- ctor public CertificateExpiredException(java.lang.String);
ctor public CertificateExpiredException();
+ ctor public CertificateExpiredException(java.lang.String);
}
public class CertificateFactory {
@@ -48385,28 +49528,44 @@
}
public class CertificateNotYetValidException extends java.security.cert.CertificateException {
- ctor public CertificateNotYetValidException(java.lang.String);
ctor public CertificateNotYetValidException();
+ ctor public CertificateNotYetValidException(java.lang.String);
}
public class CertificateParsingException extends java.security.cert.CertificateException {
- ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException();
+ ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException(java.lang.String, java.lang.Throwable);
ctor public CertificateParsingException(java.lang.Throwable);
}
+ public class CertificateRevokedException extends java.security.cert.CertificateException {
+ ctor public CertificateRevokedException(java.util.Date, java.security.cert.CRLReason, javax.security.auth.x500.X500Principal, java.util.Map<java.lang.String, java.security.cert.Extension>);
+ method public javax.security.auth.x500.X500Principal getAuthorityName();
+ method public java.util.Map<java.lang.String, java.security.cert.Extension> getExtensions();
+ method public java.util.Date getInvalidityDate();
+ method public java.util.Date getRevocationDate();
+ method public java.security.cert.CRLReason getRevocationReason();
+ }
+
public class CollectionCertStoreParameters implements java.security.cert.CertStoreParameters {
- ctor public CollectionCertStoreParameters();
ctor public CollectionCertStoreParameters(java.util.Collection<?>);
+ ctor public CollectionCertStoreParameters();
method public java.lang.Object clone();
method public java.util.Collection<?> getCollection();
}
+ public abstract interface Extension {
+ method public abstract void encode(java.io.OutputStream) throws java.io.IOException;
+ method public abstract java.lang.String getId();
+ method public abstract byte[] getValue();
+ method public abstract boolean isCritical();
+ }
+
public class LDAPCertStoreParameters implements java.security.cert.CertStoreParameters {
ctor public LDAPCertStoreParameters(java.lang.String, int);
- ctor public LDAPCertStoreParameters();
ctor public LDAPCertStoreParameters(java.lang.String);
+ ctor public LDAPCertStoreParameters();
method public java.lang.Object clone();
method public int getPort();
method public java.lang.String getServerName();
@@ -48473,6 +49632,19 @@
method public void setTrustAnchors(java.util.Set<java.security.cert.TrustAnchor>) throws java.security.InvalidAlgorithmParameterException;
}
+ public final class PKIXReason extends java.lang.Enum implements java.security.cert.CertPathValidatorException.Reason {
+ method public static java.security.cert.PKIXReason valueOf(java.lang.String);
+ method public static final java.security.cert.PKIXReason[] values();
+ enum_constant public static final java.security.cert.PKIXReason INVALID_KEY_USAGE;
+ enum_constant public static final java.security.cert.PKIXReason INVALID_NAME;
+ enum_constant public static final java.security.cert.PKIXReason INVALID_POLICY;
+ enum_constant public static final java.security.cert.PKIXReason NAME_CHAINING;
+ enum_constant public static final java.security.cert.PKIXReason NOT_CA_CERT;
+ enum_constant public static final java.security.cert.PKIXReason NO_TRUST_ANCHOR;
+ enum_constant public static final java.security.cert.PKIXReason PATH_TOO_LONG;
+ enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
+ }
+
public abstract interface PolicyNode {
method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
method public abstract int getDepth();
@@ -48492,8 +49664,8 @@
public class TrustAnchor {
ctor public TrustAnchor(java.security.cert.X509Certificate, byte[]);
- ctor public TrustAnchor(java.lang.String, java.security.PublicKey, byte[]);
ctor public TrustAnchor(javax.security.auth.x500.X500Principal, java.security.PublicKey, byte[]);
+ ctor public TrustAnchor(java.lang.String, java.security.PublicKey, byte[]);
method public final javax.security.auth.x500.X500Principal getCA();
method public final java.lang.String getCAName();
method public final java.security.PublicKey getCAPublicKey();
@@ -48526,6 +49698,7 @@
method public javax.security.auth.x500.X500Principal getCertificateIssuer();
method public abstract byte[] getEncoded() throws java.security.cert.CRLException;
method public abstract java.util.Date getRevocationDate();
+ method public java.security.cert.CRLReason getRevocationReason();
method public abstract java.math.BigInteger getSerialNumber();
method public abstract boolean hasExtensions();
method public abstract java.lang.String toString();
@@ -48801,8 +49974,8 @@
}
public class EllipticCurve {
- ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger, byte[]);
ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger);
+ ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger, byte[]);
method public java.math.BigInteger getA();
method public java.math.BigInteger getB();
method public java.security.spec.ECField getField();
@@ -48816,15 +49989,15 @@
}
public class InvalidKeySpecException extends java.security.GeneralSecurityException {
- ctor public InvalidKeySpecException(java.lang.String);
ctor public InvalidKeySpecException();
+ ctor public InvalidKeySpecException(java.lang.String);
ctor public InvalidKeySpecException(java.lang.String, java.lang.Throwable);
ctor public InvalidKeySpecException(java.lang.Throwable);
}
public class InvalidParameterSpecException extends java.security.GeneralSecurityException {
- ctor public InvalidParameterSpecException(java.lang.String);
ctor public InvalidParameterSpecException();
+ ctor public InvalidParameterSpecException(java.lang.String);
}
public abstract interface KeySpec {
@@ -48845,8 +50018,8 @@
}
public class PSSParameterSpec implements java.security.spec.AlgorithmParameterSpec {
- ctor public PSSParameterSpec(int);
ctor public PSSParameterSpec(java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec, int, int);
+ ctor public PSSParameterSpec(int);
method public java.lang.String getDigestAlgorithm();
method public java.lang.String getMGFAlgorithm();
method public java.security.spec.AlgorithmParameterSpec getMGFParameters();
@@ -48915,28 +50088,28 @@
public abstract interface Array {
method public abstract void free() throws java.sql.SQLException;
method public abstract java.lang.Object getArray() throws java.sql.SQLException;
+ method public abstract java.lang.Object getArray(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getArray(long, int) throws java.sql.SQLException;
method public abstract java.lang.Object getArray(long, int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
- method public abstract java.lang.Object getArray(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract int getBaseType() throws java.sql.SQLException;
method public abstract java.lang.String getBaseTypeName() throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet() throws java.sql.SQLException;
+ method public abstract java.sql.ResultSet getResultSet(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet(long, int) throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet(long, int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
- method public abstract java.sql.ResultSet getResultSet(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
}
- public class BatchUpdateException extends java.sql.SQLException implements java.io.Serializable {
+ public class BatchUpdateException extends java.sql.SQLException {
+ ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[]);
+ ctor public BatchUpdateException(java.lang.String, java.lang.String, int[]);
+ ctor public BatchUpdateException(java.lang.String, int[]);
+ ctor public BatchUpdateException(int[]);
ctor public BatchUpdateException();
ctor public BatchUpdateException(java.lang.Throwable);
ctor public BatchUpdateException(int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, java.lang.String, int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[], java.lang.Throwable);
- ctor public BatchUpdateException(int[]);
- ctor public BatchUpdateException(java.lang.String, int[]);
- ctor public BatchUpdateException(java.lang.String, java.lang.String, int[]);
- ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[]);
method public int[] getUpdateCounts();
}
@@ -48946,8 +50119,8 @@
method public abstract java.io.InputStream getBinaryStream(long, long) throws java.sql.SQLException;
method public abstract byte[] getBytes(long, int) throws java.sql.SQLException;
method public abstract long length() throws java.sql.SQLException;
- method public abstract long position(java.sql.Blob, long) throws java.sql.SQLException;
method public abstract long position(byte[], long) throws java.sql.SQLException;
+ method public abstract long position(java.sql.Blob, long) throws java.sql.SQLException;
method public abstract java.io.OutputStream setBinaryStream(long) throws java.sql.SQLException;
method public abstract int setBytes(long, byte[]) throws java.sql.SQLException;
method public abstract int setBytes(long, byte[], int, int) throws java.sql.SQLException;
@@ -48957,8 +50130,8 @@
public abstract interface CallableStatement implements java.sql.PreparedStatement {
method public abstract java.sql.Array getArray(int) throws java.sql.SQLException;
method public abstract java.sql.Array getArray(java.lang.String) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(int) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(java.lang.String) throws java.sql.SQLException;
@@ -49053,9 +50226,9 @@
method public abstract void setNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(java.lang.String, java.lang.Object, int, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
method public abstract void setRowId(java.lang.String, java.sql.RowId) throws java.sql.SQLException;
method public abstract void setSQLXML(java.lang.String, java.sql.SQLXML) throws java.sql.SQLException;
method public abstract void setShort(java.lang.String, short) throws java.sql.SQLException;
@@ -49084,8 +50257,8 @@
method public abstract java.io.Reader getCharacterStream(long, long) throws java.sql.SQLException;
method public abstract java.lang.String getSubString(long, int) throws java.sql.SQLException;
method public abstract long length() throws java.sql.SQLException;
- method public abstract long position(java.sql.Clob, long) throws java.sql.SQLException;
method public abstract long position(java.lang.String, long) throws java.sql.SQLException;
+ method public abstract long position(java.sql.Clob, long) throws java.sql.SQLException;
method public abstract java.io.OutputStream setAsciiStream(long) throws java.sql.SQLException;
method public abstract java.io.Writer setCharacterStream(long) throws java.sql.SQLException;
method public abstract int setString(long, java.lang.String) throws java.sql.SQLException;
@@ -49123,10 +50296,10 @@
method public abstract java.sql.CallableStatement prepareCall(java.lang.String, int, int) throws java.sql.SQLException;
method public abstract java.sql.CallableStatement prepareCall(java.lang.String, int, int, int) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String) throws java.sql.SQLException;
- method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int) throws java.sql.SQLException;
- method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int[]) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int, int) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int, int, int) throws java.sql.SQLException;
+ method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int[]) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, java.lang.String[]) throws java.sql.SQLException;
method public abstract void releaseSavepoint(java.sql.Savepoint) throws java.sql.SQLException;
method public abstract void rollback() throws java.sql.SQLException;
@@ -49148,7 +50321,7 @@
field public static final int TRANSACTION_SERIALIZABLE = 8; // 0x8
}
- public class DataTruncation extends java.sql.SQLWarning implements java.io.Serializable {
+ public class DataTruncation extends java.sql.SQLWarning {
ctor public DataTruncation(int, boolean, boolean, int, int);
ctor public DataTruncation(int, boolean, boolean, int, int, java.lang.Throwable);
method public int getDataSize();
@@ -49410,17 +50583,17 @@
}
public class DriverManager {
- method public static void deregisterDriver(java.sql.Driver) throws java.sql.SQLException;
- method public static java.sql.Connection getConnection(java.lang.String) throws java.sql.SQLException;
+ method public static synchronized void deregisterDriver(java.sql.Driver) throws java.sql.SQLException;
method public static java.sql.Connection getConnection(java.lang.String, java.util.Properties) throws java.sql.SQLException;
method public static java.sql.Connection getConnection(java.lang.String, java.lang.String, java.lang.String) throws java.sql.SQLException;
+ method public static java.sql.Connection getConnection(java.lang.String) throws java.sql.SQLException;
method public static java.sql.Driver getDriver(java.lang.String) throws java.sql.SQLException;
method public static java.util.Enumeration<java.sql.Driver> getDrivers();
method public static deprecated java.io.PrintStream getLogStream();
method public static java.io.PrintWriter getLogWriter();
method public static int getLoginTimeout();
method public static void println(java.lang.String);
- method public static void registerDriver(java.sql.Driver) throws java.sql.SQLException;
+ method public static synchronized void registerDriver(java.sql.Driver) throws java.sql.SQLException;
method public static deprecated void setLogStream(java.io.PrintStream);
method public static void setLogWriter(java.io.PrintWriter);
method public static void setLoginTimeout(int);
@@ -49499,8 +50672,8 @@
method public abstract void setNString(int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(int, int) throws java.sql.SQLException;
method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int, int) throws java.sql.SQLException;
method public abstract void setRef(int, java.sql.Ref) throws java.sql.SQLException;
method public abstract void setRowId(int, java.sql.RowId) throws java.sql.SQLException;
@@ -49517,8 +50690,8 @@
public abstract interface Ref {
method public abstract java.lang.String getBaseTypeName() throws java.sql.SQLException;
- method public abstract java.lang.Object getObject() throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
+ method public abstract java.lang.Object getObject() throws java.sql.SQLException;
method public abstract void setObject(java.lang.Object) throws java.sql.SQLException;
}
@@ -49536,10 +50709,10 @@
method public abstract java.sql.Array getArray(java.lang.String) throws java.sql.SQLException;
method public abstract java.io.InputStream getAsciiStream(int) throws java.sql.SQLException;
method public abstract java.io.InputStream getAsciiStream(java.lang.String) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract java.io.InputStream getBinaryStream(int) throws java.sql.SQLException;
method public abstract java.io.InputStream getBinaryStream(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(int) throws java.sql.SQLException;
@@ -49557,8 +50730,8 @@
method public abstract int getConcurrency() throws java.sql.SQLException;
method public abstract java.lang.String getCursorName() throws java.sql.SQLException;
method public abstract java.sql.Date getDate(int) throws java.sql.SQLException;
- method public abstract java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Date getDate(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Date getDate(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract double getDouble(int) throws java.sql.SQLException;
method public abstract double getDouble(java.lang.String) throws java.sql.SQLException;
@@ -49579,8 +50752,8 @@
method public abstract java.lang.String getNString(int) throws java.sql.SQLException;
method public abstract java.lang.String getNString(java.lang.String) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(int) throws java.sql.SQLException;
- method public abstract java.lang.Object getObject(int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.lang.Object getObject(int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.lang.String, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.sql.Ref getRef(int) throws java.sql.SQLException;
method public abstract java.sql.Ref getRef(java.lang.String) throws java.sql.SQLException;
@@ -49595,12 +50768,12 @@
method public abstract java.lang.String getString(int) throws java.sql.SQLException;
method public abstract java.lang.String getString(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(int) throws java.sql.SQLException;
- method public abstract java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(int) throws java.sql.SQLException;
- method public abstract java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract int getType() throws java.sql.SQLException;
method public abstract java.net.URL getURL(int) throws java.sql.SQLException;
@@ -49690,10 +50863,10 @@
method public abstract void updateNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void updateNull(int) throws java.sql.SQLException;
method public abstract void updateNull(java.lang.String) throws java.sql.SQLException;
- method public abstract void updateObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateObject(int, java.lang.Object, int) throws java.sql.SQLException;
- method public abstract void updateObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
+ method public abstract void updateObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void updateObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateRef(int, java.sql.Ref) throws java.sql.SQLException;
method public abstract void updateRef(java.lang.String, java.sql.Ref) throws java.sql.SQLException;
method public abstract void updateRow() throws java.sql.SQLException;
@@ -49772,10 +50945,10 @@
ctor public SQLClientInfoException(java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
ctor public SQLClientInfoException(java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
ctor public SQLClientInfoException(java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
- ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
- ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
ctor public SQLClientInfoException(java.lang.String, java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
ctor public SQLClientInfoException(java.lang.String, java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
+ ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
+ ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
method public java.util.Map<java.lang.String, java.sql.ClientInfoStatus> getFailedProperties();
}
@@ -49796,11 +50969,11 @@
ctor public SQLDataException(java.lang.String, java.lang.String, int, java.lang.Throwable);
}
- public class SQLException extends java.lang.Exception implements java.lang.Iterable java.io.Serializable {
- ctor public SQLException();
- ctor public SQLException(java.lang.String);
- ctor public SQLException(java.lang.String, java.lang.String);
+ public class SQLException extends java.lang.Exception implements java.lang.Iterable {
ctor public SQLException(java.lang.String, java.lang.String, int);
+ ctor public SQLException(java.lang.String, java.lang.String);
+ ctor public SQLException(java.lang.String);
+ ctor public SQLException();
ctor public SQLException(java.lang.Throwable);
ctor public SQLException(java.lang.String, java.lang.Throwable);
ctor public SQLException(java.lang.String, java.lang.String, java.lang.Throwable);
@@ -49927,7 +51100,7 @@
method public abstract void writeURL(java.net.URL) throws java.sql.SQLException;
}
- public final class SQLPermission extends java.security.BasicPermission implements java.security.Guard java.io.Serializable {
+ public final class SQLPermission extends java.security.BasicPermission {
ctor public SQLPermission(java.lang.String);
ctor public SQLPermission(java.lang.String, java.lang.String);
}
@@ -49998,11 +51171,11 @@
ctor public SQLTransientException(java.lang.String, java.lang.String, int, java.lang.Throwable);
}
- public class SQLWarning extends java.sql.SQLException implements java.io.Serializable {
- ctor public SQLWarning();
- ctor public SQLWarning(java.lang.String);
- ctor public SQLWarning(java.lang.String, java.lang.String);
+ public class SQLWarning extends java.sql.SQLException {
ctor public SQLWarning(java.lang.String, java.lang.String, int);
+ ctor public SQLWarning(java.lang.String, java.lang.String);
+ ctor public SQLWarning(java.lang.String);
+ ctor public SQLWarning();
ctor public SQLWarning(java.lang.Throwable);
ctor public SQLWarning(java.lang.String, java.lang.Throwable);
ctor public SQLWarning(java.lang.String, java.lang.String, java.lang.Throwable);
@@ -50091,15 +51264,15 @@
}
public class Timestamp extends java.util.Date {
- ctor public deprecated Timestamp(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ ctor public deprecated Timestamp(int, int, int, int, int, int, int);
ctor public Timestamp(long);
method public boolean after(java.sql.Timestamp);
method public boolean before(java.sql.Timestamp);
method public int compareTo(java.sql.Timestamp);
method public boolean equals(java.sql.Timestamp);
method public int getNanos();
- method public void setNanos(int) throws java.lang.IllegalArgumentException;
- method public static java.sql.Timestamp valueOf(java.lang.String) throws java.lang.IllegalArgumentException;
+ method public void setNanos(int);
+ method public static java.sql.Timestamp valueOf(java.lang.String);
}
public class Types {
@@ -50179,11 +51352,11 @@
}
public class AttributedString {
+ ctor public AttributedString(java.lang.String);
+ ctor public AttributedString(java.lang.String, java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>);
ctor public AttributedString(java.text.AttributedCharacterIterator);
ctor public AttributedString(java.text.AttributedCharacterIterator, int, int);
ctor public AttributedString(java.text.AttributedCharacterIterator, int, int, java.text.AttributedCharacterIterator.Attribute[]);
- ctor public AttributedString(java.lang.String);
- ctor public AttributedString(java.lang.String, java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>);
method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, java.lang.Object);
method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, java.lang.Object, int, int);
method public void addAttributes(java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>, int, int);
@@ -50193,9 +51366,9 @@
}
public final class Bidi {
+ ctor public Bidi(java.lang.String, int);
ctor public Bidi(java.text.AttributedCharacterIterator);
ctor public Bidi(char[], int, byte[], int, int, int);
- ctor public Bidi(java.lang.String, int);
method public boolean baseIsLeftToRight();
method public java.text.Bidi createLineBidi(int, int);
method public int getBaseLevel();
@@ -50222,7 +51395,7 @@
method public abstract int current();
method public abstract int first();
method public abstract int following(int);
- method public static java.util.Locale[] getAvailableLocales();
+ method public static synchronized java.util.Locale[] getAvailableLocales();
method public static java.text.BreakIterator getCharacterInstance();
method public static java.text.BreakIterator getCharacterInstance(java.util.Locale);
method public static java.text.BreakIterator getLineInstance();
@@ -50234,8 +51407,8 @@
method public static java.text.BreakIterator getWordInstance(java.util.Locale);
method public boolean isBoundary(int);
method public abstract int last();
- method public abstract int next();
method public abstract int next(int);
+ method public abstract int next();
method public int preceding(int);
method public abstract int previous();
method public void setText(java.lang.String);
@@ -50258,11 +51431,11 @@
}
public class ChoiceFormat extends java.text.NumberFormat {
- ctor public ChoiceFormat(double[], java.lang.String[]);
ctor public ChoiceFormat(java.lang.String);
+ ctor public ChoiceFormat(double[], java.lang.String[]);
method public void applyPattern(java.lang.String);
- method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
+ method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.Object[] getFormats();
method public double[] getLimits();
method public static final double nextDouble(double);
@@ -50282,8 +51455,8 @@
method public void reset();
method public static final short secondaryOrder(int);
method public void setOffset(int);
- method public void setText(java.text.CharacterIterator);
method public void setText(java.lang.String);
+ method public void setText(java.text.CharacterIterator);
method public static final short tertiaryOrder(int);
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -50298,18 +51471,18 @@
public abstract class Collator implements java.lang.Cloneable java.util.Comparator {
ctor protected Collator();
method public java.lang.Object clone();
- method public int compare(java.lang.Object, java.lang.Object);
method public abstract int compare(java.lang.String, java.lang.String);
+ method public int compare(java.lang.Object, java.lang.Object);
method public boolean equals(java.lang.String, java.lang.String);
- method public static java.util.Locale[] getAvailableLocales();
+ method public static synchronized java.util.Locale[] getAvailableLocales();
method public abstract java.text.CollationKey getCollationKey(java.lang.String);
- method public int getDecomposition();
- method public static java.text.Collator getInstance();
- method public static java.text.Collator getInstance(java.util.Locale);
- method public int getStrength();
+ method public synchronized int getDecomposition();
+ method public static synchronized java.text.Collator getInstance();
+ method public static synchronized java.text.Collator getInstance(java.util.Locale);
+ method public synchronized int getStrength();
method public abstract int hashCode();
- method public void setDecomposition(int);
- method public void setStrength(int);
+ method public synchronized void setDecomposition(int);
+ method public synchronized void setStrength(int);
field public static final int CANONICAL_DECOMPOSITION = 1; // 0x1
field public static final int FULL_DECOMPOSITION = 2; // 0x2
field public static final int IDENTICAL = 3; // 0x3
@@ -50322,8 +51495,8 @@
public abstract class DateFormat extends java.text.Format {
ctor protected DateFormat();
method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.String format(java.util.Date);
method public abstract java.lang.StringBuffer format(java.util.Date, java.lang.StringBuffer, java.text.FieldPosition);
+ method public final java.lang.String format(java.util.Date);
method public static java.util.Locale[] getAvailableLocales();
method public java.util.Calendar getCalendar();
method public static final java.text.DateFormat getDateInstance();
@@ -50428,9 +51601,9 @@
ctor public DecimalFormat(java.lang.String, java.text.DecimalFormatSymbols);
method public void applyLocalizedPattern(java.lang.String);
method public void applyPattern(java.lang.String);
+ method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.DecimalFormatSymbols getDecimalFormatSymbols();
method public int getGroupingSize();
method public int getMultiplier();
@@ -50466,15 +51639,17 @@
method public java.lang.String getExponentSeparator();
method public char getGroupingSeparator();
method public java.lang.String getInfinity();
- method public static java.text.DecimalFormatSymbols getInstance();
- method public static java.text.DecimalFormatSymbols getInstance(java.util.Locale);
+ method public static final java.text.DecimalFormatSymbols getInstance();
+ method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
+ method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
+ method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
@@ -50511,8 +51686,8 @@
method public final java.lang.String format(java.lang.Object);
method public abstract java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.AttributedCharacterIterator formatToCharacterIterator(java.lang.Object);
- method public java.lang.Object parseObject(java.lang.String) throws java.text.ParseException;
method public abstract java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Object parseObject(java.lang.String) throws java.text.ParseException;
}
public static class Format.Field extends java.text.AttributedCharacterIterator.Attribute {
@@ -50520,17 +51695,17 @@
}
public class MessageFormat extends java.text.Format {
- ctor public MessageFormat(java.lang.String, java.util.Locale);
ctor public MessageFormat(java.lang.String);
+ ctor public MessageFormat(java.lang.String, java.util.Locale);
method public void applyPattern(java.lang.String);
method public final java.lang.StringBuffer format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public static java.lang.String format(java.lang.String, java.lang.Object...);
+ method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.Format[] getFormats();
method public java.text.Format[] getFormatsByArgumentIndex();
method public java.util.Locale getLocale();
- method public java.lang.Object[] parse(java.lang.String) throws java.text.ParseException;
method public java.lang.Object[] parse(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Object[] parse(java.lang.String) throws java.text.ParseException;
method public java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
method public void setFormat(int, java.text.Format);
method public void setFormatByArgumentIndex(int, java.text.Format);
@@ -50561,11 +51736,11 @@
public abstract class NumberFormat extends java.text.Format {
ctor protected NumberFormat();
- method public final java.lang.String format(double);
- method public abstract java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.String format(long);
- method public abstract java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+ method public final java.lang.String format(double);
+ method public final java.lang.String format(long);
+ method public abstract java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
+ method public abstract java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public static java.util.Locale[] getAvailableLocales();
method public java.util.Currency getCurrency();
method public static final java.text.NumberFormat getCurrencyInstance();
@@ -50585,8 +51760,8 @@
method public java.math.RoundingMode getRoundingMode();
method public boolean isGroupingUsed();
method public boolean isParseIntegerOnly();
- method public java.lang.Number parse(java.lang.String) throws java.text.ParseException;
method public abstract java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Number parse(java.lang.String) throws java.text.ParseException;
method public final java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
method public void setCurrency(java.util.Currency);
method public void setGroupingUsed(boolean);
@@ -50630,10 +51805,10 @@
public class RuleBasedCollator extends java.text.Collator {
ctor public RuleBasedCollator(java.lang.String) throws java.text.ParseException;
- method public int compare(java.lang.String, java.lang.String);
- method public java.text.CollationElementIterator getCollationElementIterator(java.text.CharacterIterator);
+ method public synchronized int compare(java.lang.String, java.lang.String);
method public java.text.CollationElementIterator getCollationElementIterator(java.lang.String);
- method public java.text.CollationKey getCollationKey(java.lang.String);
+ method public java.text.CollationElementIterator getCollationElementIterator(java.text.CharacterIterator);
+ method public synchronized java.text.CollationKey getCollationKey(java.lang.String);
method public java.lang.String getRules();
method public int hashCode();
}
@@ -50641,8 +51816,8 @@
public class SimpleDateFormat extends java.text.DateFormat {
ctor public SimpleDateFormat();
ctor public SimpleDateFormat(java.lang.String);
- ctor public SimpleDateFormat(java.lang.String, java.text.DateFormatSymbols);
ctor public SimpleDateFormat(java.lang.String, java.util.Locale);
+ ctor public SimpleDateFormat(java.lang.String, java.text.DateFormatSymbols);
method public void applyLocalizedPattern(java.lang.String);
method public void applyPattern(java.lang.String);
method public java.lang.StringBuffer format(java.util.Date, java.lang.StringBuffer, java.text.FieldPosition);
@@ -50789,7 +51964,7 @@
method public int size();
}
- public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.RandomAccess java.io.Serializable {
+ public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
ctor public ArrayList(int);
ctor public ArrayList();
ctor public ArrayList(java.util.Collection<? extends E>);
@@ -50802,109 +51977,109 @@
public class Arrays {
method public static java.util.List<T> asList(T...);
- method public static int binarySearch(byte[], byte);
- method public static int binarySearch(byte[], int, int, byte);
+ method public static int binarySearch(long[], long);
+ method public static int binarySearch(long[], int, int, long);
+ method public static int binarySearch(int[], int);
+ method public static int binarySearch(int[], int, int, int);
+ method public static int binarySearch(short[], short);
+ method public static int binarySearch(short[], int, int, short);
method public static int binarySearch(char[], char);
method public static int binarySearch(char[], int, int, char);
+ method public static int binarySearch(byte[], byte);
+ method public static int binarySearch(byte[], int, int, byte);
method public static int binarySearch(double[], double);
method public static int binarySearch(double[], int, int, double);
method public static int binarySearch(float[], float);
method public static int binarySearch(float[], int, int, float);
- method public static int binarySearch(int[], int);
- method public static int binarySearch(int[], int, int, int);
- method public static int binarySearch(long[], long);
- method public static int binarySearch(long[], int, int, long);
method public static int binarySearch(java.lang.Object[], java.lang.Object);
method public static int binarySearch(java.lang.Object[], int, int, java.lang.Object);
method public static int binarySearch(T[], T, java.util.Comparator<? super T>);
method public static int binarySearch(T[], int, int, T, java.util.Comparator<? super T>);
- method public static int binarySearch(short[], short);
- method public static int binarySearch(short[], int, int, short);
- method public static boolean[] copyOf(boolean[], int);
- method public static byte[] copyOf(byte[], int);
- method public static char[] copyOf(char[], int);
- method public static double[] copyOf(double[], int);
- method public static float[] copyOf(float[], int);
- method public static int[] copyOf(int[], int);
- method public static long[] copyOf(long[], int);
- method public static short[] copyOf(short[], int);
method public static T[] copyOf(T[], int);
method public static T[] copyOf(U[], int, java.lang.Class<? extends T[]>);
- method public static boolean[] copyOfRange(boolean[], int, int);
- method public static byte[] copyOfRange(byte[], int, int);
- method public static char[] copyOfRange(char[], int, int);
- method public static double[] copyOfRange(double[], int, int);
- method public static float[] copyOfRange(float[], int, int);
- method public static int[] copyOfRange(int[], int, int);
- method public static long[] copyOfRange(long[], int, int);
- method public static short[] copyOfRange(short[], int, int);
+ method public static byte[] copyOf(byte[], int);
+ method public static short[] copyOf(short[], int);
+ method public static int[] copyOf(int[], int);
+ method public static long[] copyOf(long[], int);
+ method public static char[] copyOf(char[], int);
+ method public static float[] copyOf(float[], int);
+ method public static double[] copyOf(double[], int);
+ method public static boolean[] copyOf(boolean[], int);
method public static T[] copyOfRange(T[], int, int);
method public static T[] copyOfRange(U[], int, int, java.lang.Class<? extends T[]>);
+ method public static byte[] copyOfRange(byte[], int, int);
+ method public static short[] copyOfRange(short[], int, int);
+ method public static int[] copyOfRange(int[], int, int);
+ method public static long[] copyOfRange(long[], int, int);
+ method public static char[] copyOfRange(char[], int, int);
+ method public static float[] copyOfRange(float[], int, int);
+ method public static double[] copyOfRange(double[], int, int);
+ method public static boolean[] copyOfRange(boolean[], int, int);
method public static boolean deepEquals(java.lang.Object[], java.lang.Object[]);
method public static int deepHashCode(java.lang.Object[]);
method public static java.lang.String deepToString(java.lang.Object[]);
- method public static boolean equals(byte[], byte[]);
+ method public static boolean equals(long[], long[]);
+ method public static boolean equals(int[], int[]);
method public static boolean equals(short[], short[]);
method public static boolean equals(char[], char[]);
- method public static boolean equals(int[], int[]);
- method public static boolean equals(long[], long[]);
- method public static boolean equals(float[], float[]);
- method public static boolean equals(double[], double[]);
+ method public static boolean equals(byte[], byte[]);
method public static boolean equals(boolean[], boolean[]);
+ method public static boolean equals(double[], double[]);
+ method public static boolean equals(float[], float[]);
method public static boolean equals(java.lang.Object[], java.lang.Object[]);
- method public static void fill(byte[], byte);
- method public static void fill(byte[], int, int, byte);
+ method public static void fill(long[], long);
+ method public static void fill(long[], int, int, long);
+ method public static void fill(int[], int);
+ method public static void fill(int[], int, int, int);
method public static void fill(short[], short);
method public static void fill(short[], int, int, short);
method public static void fill(char[], char);
method public static void fill(char[], int, int, char);
- method public static void fill(int[], int);
- method public static void fill(int[], int, int, int);
- method public static void fill(long[], long);
- method public static void fill(long[], int, int, long);
- method public static void fill(float[], float);
- method public static void fill(float[], int, int, float);
- method public static void fill(double[], double);
- method public static void fill(double[], int, int, double);
+ method public static void fill(byte[], byte);
+ method public static void fill(byte[], int, int, byte);
method public static void fill(boolean[], boolean);
method public static void fill(boolean[], int, int, boolean);
+ method public static void fill(double[], double);
+ method public static void fill(double[], int, int, double);
+ method public static void fill(float[], float);
+ method public static void fill(float[], int, int, float);
method public static void fill(java.lang.Object[], java.lang.Object);
method public static void fill(java.lang.Object[], int, int, java.lang.Object);
- method public static int hashCode(boolean[]);
+ method public static int hashCode(long[]);
method public static int hashCode(int[]);
method public static int hashCode(short[]);
method public static int hashCode(char[]);
method public static int hashCode(byte[]);
- method public static int hashCode(long[]);
+ method public static int hashCode(boolean[]);
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
- method public static void sort(byte[]);
- method public static void sort(byte[], int, int);
- method public static void sort(char[]);
- method public static void sort(char[], int, int);
- method public static void sort(double[]);
- method public static void sort(double[], int, int);
- method public static void sort(float[]);
- method public static void sort(float[], int, int);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
method public static void sort(long[], int, int);
method public static void sort(short[]);
method public static void sort(short[], int, int);
+ method public static void sort(char[]);
+ method public static void sort(char[], int, int);
+ method public static void sort(byte[]);
+ method public static void sort(byte[], int, int);
+ method public static void sort(float[]);
+ method public static void sort(float[], int, int);
+ method public static void sort(double[]);
+ method public static void sort(double[], int, int);
method public static void sort(java.lang.Object[]);
method public static void sort(java.lang.Object[], int, int);
- method public static void sort(T[], int, int, java.util.Comparator<? super T>);
method public static void sort(T[], java.util.Comparator<? super T>);
- method public static java.lang.String toString(boolean[]);
- method public static java.lang.String toString(byte[]);
- method public static java.lang.String toString(char[]);
- method public static java.lang.String toString(double[]);
- method public static java.lang.String toString(float[]);
- method public static java.lang.String toString(int[]);
+ method public static void sort(T[], int, int, java.util.Comparator<? super T>);
method public static java.lang.String toString(long[]);
+ method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
+ method public static java.lang.String toString(char[]);
+ method public static java.lang.String toString(byte[]);
+ method public static java.lang.String toString(boolean[]);
+ method public static java.lang.String toString(float[]);
+ method public static java.lang.String toString(double[]);
method public static java.lang.String toString(java.lang.Object[]);
}
@@ -50915,8 +52090,8 @@
method public void andNot(java.util.BitSet);
method public int cardinality();
method public void clear(int);
- method public void clear();
method public void clear(int, int);
+ method public void clear();
method public java.lang.Object clone();
method public void flip(int);
method public void flip(int, int);
@@ -50932,8 +52107,8 @@
method public int previousSetBit(int);
method public void set(int);
method public void set(int, boolean);
- method public void set(int, int, boolean);
method public void set(int, int);
+ method public void set(int, int, boolean);
method public int size();
method public byte[] toByteArray();
method public long[] toLongArray();
@@ -50965,10 +52140,10 @@
method public java.util.Map<java.lang.String, java.lang.Integer> getDisplayNames(int, int, java.util.Locale);
method public int getFirstDayOfWeek();
method public abstract int getGreatestMinimum(int);
- method public static synchronized java.util.Calendar getInstance();
- method public static synchronized java.util.Calendar getInstance(java.util.Locale);
- method public static synchronized java.util.Calendar getInstance(java.util.TimeZone);
- method public static synchronized java.util.Calendar getInstance(java.util.TimeZone, java.util.Locale);
+ method public static java.util.Calendar getInstance();
+ method public static java.util.Calendar getInstance(java.util.TimeZone);
+ method public static java.util.Calendar getInstance(java.util.Locale);
+ method public static java.util.Calendar getInstance(java.util.TimeZone, java.util.Locale);
method public abstract int getLeastMaximum(int);
method public abstract int getMaximum(int);
method public int getMinimalDaysInFirstWeek();
@@ -50976,11 +52151,14 @@
method public final java.util.Date getTime();
method public long getTimeInMillis();
method public java.util.TimeZone getTimeZone();
+ method public int getWeekYear();
+ method public int getWeeksInWeekYear();
method protected final int internalGet(int);
method public boolean isLenient();
method public final boolean isSet(int);
- method public void roll(int, int);
+ method public boolean isWeekDateSupported();
method public abstract void roll(int, boolean);
+ method public void roll(int, int);
method public void set(int, int);
method public final void set(int, int, int);
method public final void set(int, int, int, int, int);
@@ -50991,6 +52169,7 @@
method public final void setTime(java.util.Date);
method public void setTimeInMillis(long);
method public void setTimeZone(java.util.TimeZone);
+ method public void setWeekDate(int, int, int);
field public static final int ALL_STYLES = 0; // 0x0
field public static final int AM = 0; // 0x0
field public static final int AM_PM = 9; // 0x9
@@ -51107,15 +52286,15 @@
method public static java.util.Collection<T> synchronizedCollection(java.util.Collection<T>);
method public static java.util.List<T> synchronizedList(java.util.List<T>);
method public static java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>);
- method public static java.util.Set<E> synchronizedSet(java.util.Set<E>);
+ method public static java.util.Set<T> synchronizedSet(java.util.Set<T>);
method public static java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>);
- method public static java.util.SortedSet<E> synchronizedSortedSet(java.util.SortedSet<E>);
- method public static java.util.Collection<E> unmodifiableCollection(java.util.Collection<? extends E>);
- method public static java.util.List<E> unmodifiableList(java.util.List<? extends E>);
+ method public static java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>);
+ method public static java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>);
+ method public static java.util.List<T> unmodifiableList(java.util.List<? extends T>);
method public static java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>);
- method public static java.util.Set<E> unmodifiableSet(java.util.Set<? extends E>);
+ method public static java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>);
method public static java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>);
- method public static java.util.SortedSet<E> unmodifiableSortedSet(java.util.SortedSet<E>);
+ method public static java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>);
field public static final java.util.List EMPTY_LIST;
field public static final java.util.Map EMPTY_MAP;
field public static final java.util.Set EMPTY_SET;
@@ -51129,8 +52308,8 @@
public class ConcurrentModificationException extends java.lang.RuntimeException {
ctor public ConcurrentModificationException();
ctor public ConcurrentModificationException(java.lang.String);
- ctor public ConcurrentModificationException(java.lang.String, java.lang.Throwable);
ctor public ConcurrentModificationException(java.lang.Throwable);
+ ctor public ConcurrentModificationException(java.lang.String, java.lang.Throwable);
}
public final class Currency implements java.io.Serializable {
@@ -51141,16 +52320,17 @@
method public java.lang.String getDisplayName(java.util.Locale);
method public static java.util.Currency getInstance(java.lang.String);
method public static java.util.Currency getInstance(java.util.Locale);
+ method public int getNumericCode();
method public java.lang.String getSymbol();
method public java.lang.String getSymbol(java.util.Locale);
}
public class Date implements java.lang.Cloneable java.lang.Comparable java.io.Serializable {
ctor public Date();
+ ctor public Date(long);
ctor public deprecated Date(int, int, int);
ctor public deprecated Date(int, int, int, int, int);
ctor public deprecated Date(int, int, int, int, int, int);
- ctor public Date(long);
ctor public deprecated Date(java.lang.String);
method public static deprecated long UTC(int, int, int, int, int, int);
method public boolean after(java.util.Date);
@@ -51228,7 +52408,7 @@
ctor public EmptyStackException();
}
- public class EnumMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
+ public class EnumMap extends java.util.AbstractMap implements java.lang.Cloneable java.io.Serializable {
ctor public EnumMap(java.lang.Class<K>);
ctor public EnumMap(java.util.EnumMap<K, ? extends V>);
ctor public EnumMap(java.util.Map<K, ? extends V>);
@@ -51261,8 +52441,8 @@
}
public abstract class EventListenerProxy implements java.util.EventListener {
- ctor public EventListenerProxy(java.util.EventListener);
- method public java.util.EventListener getListener();
+ ctor public EventListenerProxy(T);
+ method public T getListener();
}
public class EventObject implements java.io.Serializable {
@@ -51271,14 +52451,14 @@
field protected transient java.lang.Object source;
}
- public class FormatFlagsConversionMismatchException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class FormatFlagsConversionMismatchException extends java.util.IllegalFormatException {
ctor public FormatFlagsConversionMismatchException(java.lang.String, char);
method public char getConversion();
method public java.lang.String getFlags();
}
public abstract interface Formattable {
- method public abstract void formatTo(java.util.Formatter, int, int, int) throws java.util.IllegalFormatException;
+ method public abstract void formatTo(java.util.Formatter, int, int, int);
}
public class FormattableFlags {
@@ -51298,10 +52478,10 @@
ctor public Formatter(java.io.File) throws java.io.FileNotFoundException;
ctor public Formatter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
ctor public Formatter(java.io.File, java.lang.String, java.util.Locale) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public Formatter(java.io.PrintStream);
ctor public Formatter(java.io.OutputStream);
ctor public Formatter(java.io.OutputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
ctor public Formatter(java.io.OutputStream, java.lang.String, java.util.Locale) throws java.io.UnsupportedEncodingException;
- ctor public Formatter(java.io.PrintStream);
method public void close();
method public void flush();
method public java.util.Formatter format(java.lang.String, java.lang.Object...);
@@ -51318,18 +52498,18 @@
enum_constant public static final java.util.Formatter.BigDecimalLayoutForm SCIENTIFIC;
}
- public class FormatterClosedException extends java.lang.IllegalStateException implements java.io.Serializable {
+ public class FormatterClosedException extends java.lang.IllegalStateException {
ctor public FormatterClosedException();
}
public class GregorianCalendar extends java.util.Calendar {
ctor public GregorianCalendar();
+ ctor public GregorianCalendar(java.util.TimeZone);
+ ctor public GregorianCalendar(java.util.Locale);
+ ctor public GregorianCalendar(java.util.TimeZone, java.util.Locale);
ctor public GregorianCalendar(int, int, int);
ctor public GregorianCalendar(int, int, int, int, int);
ctor public GregorianCalendar(int, int, int, int, int, int);
- ctor public GregorianCalendar(java.util.Locale);
- ctor public GregorianCalendar(java.util.TimeZone);
- ctor public GregorianCalendar(java.util.TimeZone, java.util.Locale);
method public void add(int, int);
method protected void computeFields();
method protected void computeTime();
@@ -51339,16 +52519,17 @@
method public int getMaximum(int);
method public int getMinimum(int);
method public boolean isLeapYear(int);
+ method public final boolean isWeekDateSupported();
method public void roll(int, boolean);
method public void setGregorianChange(java.util.Date);
field public static final int AD = 1; // 0x1
field public static final int BC = 0; // 0x0
}
- public class HashMap extends java.util.AbstractMap implements java.lang.Cloneable java.io.Serializable {
- ctor public HashMap();
- ctor public HashMap(int);
+ public class HashMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
ctor public HashMap(int, float);
+ ctor public HashMap(int);
+ ctor public HashMap();
ctor public HashMap(java.util.Map<? extends K, ? extends V>);
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
@@ -51356,36 +52537,36 @@
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
ctor public HashSet();
- ctor public HashSet(int);
- ctor public HashSet(int, float);
ctor public HashSet(java.util.Collection<? extends E>);
+ ctor public HashSet(int, float);
+ ctor public HashSet(int);
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
- ctor public Hashtable();
- ctor public Hashtable(int);
ctor public Hashtable(int, float);
+ ctor public Hashtable(int);
+ ctor public Hashtable();
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
- method public boolean contains(java.lang.Object);
+ method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
- method public synchronized boolean containsValue(java.lang.Object);
+ method public boolean containsValue(java.lang.Object);
method public synchronized java.util.Enumeration<V> elements();
- method public synchronized java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+ method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized V get(java.lang.Object);
method public synchronized boolean isEmpty();
- method public synchronized java.util.Set<K> keySet();
+ method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
method public synchronized int size();
- method public synchronized java.util.Collection<V> values();
+ method public java.util.Collection<V> values();
}
public class IdentityHashMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -51396,21 +52577,21 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
}
- public class IllegalFormatCodePointException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatCodePointException extends java.util.IllegalFormatException {
ctor public IllegalFormatCodePointException(int);
method public int getCodePoint();
}
- public class IllegalFormatConversionException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatConversionException extends java.util.IllegalFormatException {
ctor public IllegalFormatConversionException(char, java.lang.Class<?>);
method public java.lang.Class<?> getArgumentClass();
method public char getConversion();
}
- public class IllegalFormatException extends java.lang.IllegalArgumentException implements java.io.Serializable {
+ public class IllegalFormatException extends java.lang.IllegalArgumentException {
}
- public class IllegalFormatFlagsException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatFlagsException extends java.util.IllegalFormatException {
ctor public IllegalFormatFlagsException(java.lang.String);
method public java.lang.String getFlags();
}
@@ -51432,14 +52613,14 @@
method public int getErrorIndex();
}
- public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable {
+ public class InputMismatchException extends java.util.NoSuchElementException {
ctor public InputMismatchException();
ctor public InputMismatchException(java.lang.String);
}
public class InvalidPropertiesFormatException extends java.io.IOException {
- ctor public InvalidPropertiesFormatException(java.lang.String);
ctor public InvalidPropertiesFormatException(java.lang.Throwable);
+ ctor public InvalidPropertiesFormatException(java.lang.String);
}
public abstract interface Iterator {
@@ -51448,23 +52629,23 @@
method public abstract void remove();
}
- public class LinkedHashMap extends java.util.HashMap {
- ctor public LinkedHashMap();
- ctor public LinkedHashMap(int);
+ public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
ctor public LinkedHashMap(int, float);
- ctor public LinkedHashMap(int, float, boolean);
+ ctor public LinkedHashMap(int);
+ ctor public LinkedHashMap();
ctor public LinkedHashMap(java.util.Map<? extends K, ? extends V>);
+ ctor public LinkedHashMap(int, float, boolean);
method protected boolean removeEldestEntry(java.util.Map.Entry<K, V>);
}
public class LinkedHashSet extends java.util.HashSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
- ctor public LinkedHashSet();
- ctor public LinkedHashSet(int);
ctor public LinkedHashSet(int, float);
+ ctor public LinkedHashSet(int);
+ ctor public LinkedHashSet();
ctor public LinkedHashSet(java.util.Collection<? extends E>);
}
- public class LinkedList extends java.util.AbstractSequentialList implements java.lang.Cloneable java.util.Deque java.util.List java.util.Queue java.io.Serializable {
+ public class LinkedList extends java.util.AbstractSequentialList implements java.lang.Cloneable java.util.Deque java.util.List java.io.Serializable {
ctor public LinkedList();
ctor public LinkedList(java.util.Collection<? extends E>);
method public void addFirst(E);
@@ -51495,10 +52676,10 @@
}
public abstract interface List implements java.util.Collection {
- method public abstract void add(int, E);
method public abstract boolean add(E);
- method public abstract boolean addAll(int, java.util.Collection<? extends E>);
+ method public abstract void add(int, E);
method public abstract boolean addAll(java.util.Collection<? extends E>);
+ method public abstract boolean addAll(int, java.util.Collection<? extends E>);
method public abstract void clear();
method public abstract boolean contains(java.lang.Object);
method public abstract boolean containsAll(java.util.Collection<?>);
@@ -51511,8 +52692,8 @@
method public abstract int lastIndexOf(java.lang.Object);
method public abstract java.util.ListIterator<E> listIterator();
method public abstract java.util.ListIterator<E> listIterator(int);
- method public abstract E remove(int);
method public abstract boolean remove(java.lang.Object);
+ method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
@@ -51542,14 +52723,15 @@
}
public final class Locale implements java.lang.Cloneable java.io.Serializable {
- ctor public Locale(java.lang.String);
- ctor public Locale(java.lang.String, java.lang.String);
ctor public Locale(java.lang.String, java.lang.String, java.lang.String);
+ ctor public Locale(java.lang.String, java.lang.String);
+ ctor public Locale(java.lang.String);
method public java.lang.Object clone();
method public static java.util.Locale forLanguageTag(java.lang.String);
method public static java.util.Locale[] getAvailableLocales();
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
+ method public static java.util.Locale getDefault(java.util.Locale.Category);
method public final java.lang.String getDisplayCountry();
method public java.lang.String getDisplayCountry(java.util.Locale);
method public final java.lang.String getDisplayLanguage();
@@ -51562,8 +52744,8 @@
method public java.lang.String getDisplayVariant(java.util.Locale);
method public java.lang.String getExtension(char);
method public java.util.Set<java.lang.Character> getExtensionKeys();
- method public java.lang.String getISO3Country();
- method public java.lang.String getISO3Language();
+ method public java.lang.String getISO3Country() throws java.util.MissingResourceException;
+ method public java.lang.String getISO3Language() throws java.util.MissingResourceException;
method public static java.lang.String[] getISOCountries();
method public static java.lang.String[] getISOLanguages();
method public java.lang.String getLanguage();
@@ -51573,6 +52755,7 @@
method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
method public static synchronized void setDefault(java.util.Locale);
+ method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
@@ -51618,6 +52801,13 @@
method public java.util.Locale.Builder setVariant(java.lang.String);
}
+ public static final class Locale.Category extends java.lang.Enum {
+ method public static java.util.Locale.Category valueOf(java.lang.String);
+ method public static final java.util.Locale.Category[] values();
+ enum_constant public static final java.util.Locale.Category DISPLAY;
+ enum_constant public static final java.util.Locale.Category FORMAT;
+ }
+
public abstract interface Map {
method public abstract void clear();
method public abstract boolean containsKey(java.lang.Object);
@@ -51720,15 +52910,15 @@
public class Observable {
ctor public Observable();
- method public void addObserver(java.util.Observer);
- method protected void clearChanged();
- method public int countObservers();
+ method public synchronized void addObserver(java.util.Observer);
+ method protected synchronized void clearChanged();
+ method public synchronized int countObservers();
method public synchronized void deleteObserver(java.util.Observer);
method public synchronized void deleteObservers();
- method public boolean hasChanged();
+ method public synchronized boolean hasChanged();
method public void notifyObservers();
method public void notifyObservers(java.lang.Object);
- method protected void setChanged();
+ method protected synchronized void setChanged();
}
public abstract interface Observer {
@@ -51757,16 +52947,16 @@
method public java.lang.String getProperty(java.lang.String, java.lang.String);
method public void list(java.io.PrintStream);
method public void list(java.io.PrintWriter);
- method public synchronized void load(java.io.InputStream) throws java.io.IOException;
method public synchronized void load(java.io.Reader) throws java.io.IOException;
+ method public synchronized void load(java.io.InputStream) throws java.io.IOException;
method public synchronized void loadFromXML(java.io.InputStream) throws java.io.IOException, java.util.InvalidPropertiesFormatException;
method public java.util.Enumeration<?> propertyNames();
method public deprecated void save(java.io.OutputStream, java.lang.String);
- method public java.lang.Object setProperty(java.lang.String, java.lang.String);
- method public synchronized void store(java.io.OutputStream, java.lang.String) throws java.io.IOException;
- method public synchronized void store(java.io.Writer, java.lang.String) throws java.io.IOException;
+ method public synchronized java.lang.Object setProperty(java.lang.String, java.lang.String);
+ method public void store(java.io.Writer, java.lang.String) throws java.io.IOException;
+ method public void store(java.io.OutputStream, java.lang.String) throws java.io.IOException;
method public void storeToXML(java.io.OutputStream, java.lang.String) throws java.io.IOException;
- method public synchronized void storeToXML(java.io.OutputStream, java.lang.String, java.lang.String) throws java.io.IOException;
+ method public void storeToXML(java.io.OutputStream, java.lang.String, java.lang.String) throws java.io.IOException;
method public java.util.Set<java.lang.String> stringPropertyNames();
field protected java.util.Properties defaults;
}
@@ -51794,7 +52984,7 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
- method protected synchronized int next(int);
+ method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
method public double nextDouble();
@@ -51811,14 +53001,14 @@
public abstract class ResourceBundle {
ctor public ResourceBundle();
- method public static void clearCache();
- method public static void clearCache(java.lang.ClassLoader);
+ method public static final void clearCache();
+ method public static final void clearCache(java.lang.ClassLoader);
method public boolean containsKey(java.lang.String);
- method public static java.util.ResourceBundle getBundle(java.lang.String) throws java.util.MissingResourceException;
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale);
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader) throws java.util.MissingResourceException;
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.ResourceBundle.Control);
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.util.ResourceBundle.Control);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.ResourceBundle.Control);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.util.ResourceBundle.Control);
+ method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader);
method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader, java.util.ResourceBundle.Control);
method public abstract java.util.Enumeration<java.lang.String> getKeys();
method public java.util.Locale getLocale();
@@ -51835,10 +53025,10 @@
public static class ResourceBundle.Control {
ctor protected ResourceBundle.Control();
method public java.util.List<java.util.Locale> getCandidateLocales(java.lang.String, java.util.Locale);
- method public static java.util.ResourceBundle.Control getControl(java.util.List<java.lang.String>);
+ method public static final java.util.ResourceBundle.Control getControl(java.util.List<java.lang.String>);
method public java.util.Locale getFallbackLocale(java.lang.String, java.util.Locale);
method public java.util.List<java.lang.String> getFormats(java.lang.String);
- method public static java.util.ResourceBundle.Control getNoFallbackControl(java.util.List<java.lang.String>);
+ method public static final java.util.ResourceBundle.Control getNoFallbackControl(java.util.List<java.lang.String>);
method public long getTimeToLive(java.lang.String, java.util.Locale);
method public boolean needsReload(java.lang.String, java.util.Locale, java.lang.String, java.lang.ClassLoader, java.util.ResourceBundle, long);
method public java.util.ResourceBundle newBundle(java.lang.String, java.util.Locale, java.lang.String, java.lang.ClassLoader, boolean) throws java.io.IOException, java.lang.IllegalAccessException, java.lang.InstantiationException;
@@ -51852,23 +53042,25 @@
}
public final class Scanner implements java.io.Closeable java.util.Iterator {
- ctor public Scanner(java.io.File) throws java.io.FileNotFoundException;
- ctor public Scanner(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
- ctor public Scanner(java.lang.String);
+ ctor public Scanner(java.lang.Readable);
ctor public Scanner(java.io.InputStream);
ctor public Scanner(java.io.InputStream, java.lang.String);
- ctor public Scanner(java.lang.Readable);
+ ctor public Scanner(java.io.File) throws java.io.FileNotFoundException;
+ ctor public Scanner(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
+ ctor public Scanner(java.nio.file.Path) throws java.io.IOException;
+ ctor public Scanner(java.nio.file.Path, java.lang.String) throws java.io.IOException;
+ ctor public Scanner(java.lang.String);
ctor public Scanner(java.nio.channels.ReadableByteChannel);
ctor public Scanner(java.nio.channels.ReadableByteChannel, java.lang.String);
method public void close();
method public java.util.regex.Pattern delimiter();
- method public java.lang.String findInLine(java.util.regex.Pattern);
method public java.lang.String findInLine(java.lang.String);
- method public java.lang.String findWithinHorizon(java.util.regex.Pattern, int);
+ method public java.lang.String findInLine(java.util.regex.Pattern);
method public java.lang.String findWithinHorizon(java.lang.String, int);
+ method public java.lang.String findWithinHorizon(java.util.regex.Pattern, int);
method public boolean hasNext();
- method public boolean hasNext(java.util.regex.Pattern);
method public boolean hasNext(java.lang.String);
+ method public boolean hasNext(java.util.regex.Pattern);
method public boolean hasNextBigDecimal();
method public boolean hasNextBigInteger();
method public boolean hasNextBigInteger(int);
@@ -51888,8 +53080,8 @@
method public java.util.Locale locale();
method public java.util.regex.MatchResult match();
method public java.lang.String next();
- method public java.lang.String next(java.util.regex.Pattern);
method public java.lang.String next(java.lang.String);
+ method public java.lang.String next(java.util.regex.Pattern);
method public java.math.BigDecimal nextBigDecimal();
method public java.math.BigInteger nextBigInteger();
method public java.math.BigInteger nextBigInteger(int);
@@ -51956,12 +53148,12 @@
method public int getRawOffset();
method public boolean inDaylightTime(java.util.Date);
method public void setDSTSavings(int);
- method public void setEndRule(int, int, int);
method public void setEndRule(int, int, int, int);
+ method public void setEndRule(int, int, int);
method public void setEndRule(int, int, int, int, boolean);
method public void setRawOffset(int);
- method public void setStartRule(int, int, int);
method public void setStartRule(int, int, int, int);
+ method public void setStartRule(int, int, int);
method public void setStartRule(int, int, int, int, boolean);
method public void setStartYear(int);
method public boolean useDaylightTime();
@@ -51972,11 +53164,14 @@
public abstract interface SortedMap implements java.util.Map {
method public abstract java.util.Comparator<? super K> comparator();
+ method public abstract java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public abstract K firstKey();
method public abstract java.util.SortedMap<K, V> headMap(K);
+ method public abstract java.util.Set<K> keySet();
method public abstract K lastKey();
method public abstract java.util.SortedMap<K, V> subMap(K, K);
method public abstract java.util.SortedMap<K, V> tailMap(K);
+ method public abstract java.util.Collection<V> values();
}
public abstract interface SortedSet implements java.util.Set {
@@ -51998,9 +53193,9 @@
}
public class StringTokenizer implements java.util.Enumeration {
- ctor public StringTokenizer(java.lang.String);
- ctor public StringTokenizer(java.lang.String, java.lang.String);
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
+ ctor public StringTokenizer(java.lang.String, java.lang.String);
+ ctor public StringTokenizer(java.lang.String);
method public int countTokens();
method public boolean hasMoreElements();
method public boolean hasMoreTokens();
@@ -52012,21 +53207,22 @@
public abstract class TimeZone implements java.lang.Cloneable java.io.Serializable {
ctor public TimeZone();
method public java.lang.Object clone();
- method public static synchronized java.lang.String[] getAvailableIDs();
method public static synchronized java.lang.String[] getAvailableIDs(int);
+ method public static synchronized java.lang.String[] getAvailableIDs();
method public int getDSTSavings();
- method public static synchronized java.util.TimeZone getDefault();
+ method public static java.util.TimeZone getDefault();
method public final java.lang.String getDisplayName();
method public final java.lang.String getDisplayName(java.util.Locale);
method public final java.lang.String getDisplayName(boolean, int);
method public java.lang.String getDisplayName(boolean, int, java.util.Locale);
method public java.lang.String getID();
- method public int getOffset(long);
method public abstract int getOffset(int, int, int, int, int, int);
+ method public int getOffset(long);
method public abstract int getRawOffset();
method public static synchronized java.util.TimeZone getTimeZone(java.lang.String);
method public boolean hasSameRules(java.util.TimeZone);
method public abstract boolean inDaylightTime(java.util.Date);
+ method public boolean observesDaylightTime();
method public static synchronized void setDefault(java.util.TimeZone);
method public void setID(java.lang.String);
method public abstract void setRawOffset(int);
@@ -52036,14 +53232,14 @@
}
public class Timer {
- ctor public Timer(java.lang.String, boolean);
- ctor public Timer(java.lang.String);
- ctor public Timer(boolean);
ctor public Timer();
+ ctor public Timer(boolean);
+ ctor public Timer(java.lang.String);
+ ctor public Timer(java.lang.String, boolean);
method public void cancel();
method public int purge();
- method public void schedule(java.util.TimerTask, java.util.Date);
method public void schedule(java.util.TimerTask, long);
+ method public void schedule(java.util.TimerTask, java.util.Date);
method public void schedule(java.util.TimerTask, long, long);
method public void schedule(java.util.TimerTask, java.util.Date, long);
method public void scheduleAtFixedRate(java.util.TimerTask, long, long);
@@ -52062,10 +53258,10 @@
ctor public TooManyListenersException(java.lang.String);
}
- public class TreeMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.NavigableMap java.io.Serializable java.util.SortedMap {
+ public class TreeMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.NavigableMap java.io.Serializable {
ctor public TreeMap();
- ctor public TreeMap(java.util.Map<? extends K, ? extends V>);
ctor public TreeMap(java.util.Comparator<? super K>);
+ ctor public TreeMap(java.util.Map<? extends K, ? extends V>);
ctor public TreeMap(java.util.SortedMap<K, ? extends V>);
method public java.util.Map.Entry<K, V> ceilingEntry(K);
method public K ceilingKey(K);
@@ -52097,8 +53293,8 @@
public class TreeSet extends java.util.AbstractSet implements java.lang.Cloneable java.util.NavigableSet java.io.Serializable {
ctor public TreeSet();
- ctor public TreeSet(java.util.Collection<? extends E>);
ctor public TreeSet(java.util.Comparator<? super E>);
+ ctor public TreeSet(java.util.Collection<? extends E>);
ctor public TreeSet(java.util.SortedSet<E>);
method public E ceiling(E);
method public java.lang.Object clone();
@@ -52148,9 +53344,9 @@
}
public class Vector extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
- ctor public Vector();
- ctor public Vector(int);
ctor public Vector(int, int);
+ ctor public Vector(int);
+ ctor public Vector();
ctor public Vector(java.util.Collection<? extends E>);
method public synchronized void addElement(E);
method public synchronized int capacity();
@@ -52160,7 +53356,7 @@
method public java.util.Enumeration<E> elements();
method public synchronized void ensureCapacity(int);
method public synchronized E firstElement();
- method public E get(int);
+ method public synchronized E get(int);
method public synchronized int indexOf(java.lang.Object, int);
method public synchronized void insertElementAt(E, int);
method public synchronized E lastElement();
@@ -52178,9 +53374,9 @@
}
public class WeakHashMap extends java.util.AbstractMap implements java.util.Map {
- ctor public WeakHashMap();
- ctor public WeakHashMap(int);
ctor public WeakHashMap(int, float);
+ ctor public WeakHashMap(int);
+ ctor public WeakHashMap();
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
}
@@ -53418,16 +54614,16 @@
public class Attributes implements java.lang.Cloneable java.util.Map {
ctor public Attributes();
- ctor public Attributes(java.util.jar.Attributes);
ctor public Attributes(int);
+ ctor public Attributes(java.util.jar.Attributes);
method public void clear();
method public java.lang.Object clone();
method public boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
method public java.util.Set<java.util.Map.Entry<java.lang.Object, java.lang.Object>> entrySet();
method public java.lang.Object get(java.lang.Object);
- method public java.lang.String getValue(java.util.jar.Attributes.Name);
method public java.lang.String getValue(java.lang.String);
+ method public java.lang.String getValue(java.util.jar.Attributes.Name);
method public boolean isEmpty();
method public java.util.Set<java.lang.Object> keySet();
method public java.lang.Object put(java.lang.Object, java.lang.Object);
@@ -53475,19 +54671,20 @@
}
public class JarFile extends java.util.zip.ZipFile {
+ ctor public JarFile(java.lang.String) throws java.io.IOException;
+ ctor public JarFile(java.lang.String, boolean) throws java.io.IOException;
ctor public JarFile(java.io.File) throws java.io.IOException;
ctor public JarFile(java.io.File, boolean) throws java.io.IOException;
ctor public JarFile(java.io.File, boolean, int) throws java.io.IOException;
- ctor public JarFile(java.lang.String) throws java.io.IOException;
- ctor public JarFile(java.lang.String, boolean) throws java.io.IOException;
method public java.util.jar.JarEntry getJarEntry(java.lang.String);
method public java.util.jar.Manifest getManifest() throws java.io.IOException;
+ method public boolean hasClassPathAttribute() throws java.io.IOException;
field public static final java.lang.String MANIFEST_NAME = "META-INF/MANIFEST.MF";
}
public class JarInputStream extends java.util.zip.ZipInputStream {
- ctor public JarInputStream(java.io.InputStream, boolean) throws java.io.IOException;
ctor public JarInputStream(java.io.InputStream) throws java.io.IOException;
+ ctor public JarInputStream(java.io.InputStream, boolean) throws java.io.IOException;
method public java.util.jar.Manifest getManifest();
method public java.util.jar.JarEntry getNextJarEntry() throws java.io.IOException;
}
@@ -53511,7 +54708,7 @@
}
public abstract class Pack200 {
- method public static java.util.jar.Pack200.Packer newPacker();
+ method public static synchronized java.util.jar.Pack200.Packer newPacker();
method public static java.util.jar.Pack200.Unpacker newUnpacker();
}
@@ -53565,7 +54762,7 @@
public class ErrorManager {
ctor public ErrorManager();
- method public void error(java.lang.String, java.lang.Exception, int);
+ method public synchronized void error(java.lang.String, java.lang.Exception, int);
field public static final int CLOSE_FAILURE = 3; // 0x3
field public static final int FLUSH_FAILURE = 2; // 0x2
field public static final int FORMAT_FAILURE = 5; // 0x5
@@ -53575,11 +54772,11 @@
}
public class FileHandler extends java.util.logging.StreamHandler {
- ctor public FileHandler() throws java.io.IOException;
- ctor public FileHandler(java.lang.String) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, boolean) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, int, int) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, int, int, boolean) throws java.io.IOException;
+ ctor public FileHandler() throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, boolean) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, int, int) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, int, int, boolean) throws java.io.IOException, java.lang.SecurityException;
}
public abstract interface Filter {
@@ -53589,28 +54786,28 @@
public abstract class Formatter {
ctor protected Formatter();
method public abstract java.lang.String format(java.util.logging.LogRecord);
- method public java.lang.String formatMessage(java.util.logging.LogRecord);
+ method public synchronized java.lang.String formatMessage(java.util.logging.LogRecord);
method public java.lang.String getHead(java.util.logging.Handler);
method public java.lang.String getTail(java.util.logging.Handler);
}
public abstract class Handler {
ctor protected Handler();
- method public abstract void close();
+ method public abstract void close() throws java.lang.SecurityException;
method public abstract void flush();
method public java.lang.String getEncoding();
method public java.util.logging.ErrorManager getErrorManager();
method public java.util.logging.Filter getFilter();
method public java.util.logging.Formatter getFormatter();
- method public java.util.logging.Level getLevel();
+ method public synchronized java.util.logging.Level getLevel();
method public boolean isLoggable(java.util.logging.LogRecord);
method public abstract void publish(java.util.logging.LogRecord);
method protected void reportError(java.lang.String, java.lang.Exception, int);
- method public void setEncoding(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
method public void setErrorManager(java.util.logging.ErrorManager);
- method public void setFilter(java.util.logging.Filter);
- method public void setFormatter(java.util.logging.Formatter);
- method public void setLevel(java.util.logging.Level);
+ method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+ method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class Level implements java.io.Serializable {
@@ -53620,7 +54817,7 @@
method public java.lang.String getName();
method public java.lang.String getResourceBundleName();
method public final int intValue();
- method public static java.util.logging.Level parse(java.lang.String) throws java.lang.IllegalArgumentException;
+ method public static synchronized java.util.logging.Level parse(java.lang.String) throws java.lang.IllegalArgumentException;
method public final java.lang.String toString();
field public static final java.util.logging.Level ALL;
field public static final java.util.logging.Level CONFIG;
@@ -53635,18 +54832,18 @@
public class LogManager {
ctor protected LogManager();
- method public synchronized boolean addLogger(java.util.logging.Logger);
- method public void addPropertyChangeListener(java.beans.PropertyChangeListener);
- method public void checkAccess();
+ method public boolean addLogger(java.util.logging.Logger);
+ method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public void checkAccess() throws java.lang.SecurityException;
method public static java.util.logging.LogManager getLogManager();
- method public synchronized java.util.logging.Logger getLogger(java.lang.String);
- method public synchronized java.util.Enumeration<java.lang.String> getLoggerNames();
- method public static java.util.logging.LoggingMXBean getLoggingMXBean();
+ method public java.util.logging.Logger getLogger(java.lang.String);
+ method public java.util.Enumeration<java.lang.String> getLoggerNames();
+ method public static synchronized java.util.logging.LoggingMXBean getLoggingMXBean();
method public java.lang.String getProperty(java.lang.String);
- method public void readConfiguration() throws java.io.IOException;
- method public void readConfiguration(java.io.InputStream) throws java.io.IOException;
- method public void removePropertyChangeListener(java.beans.PropertyChangeListener);
- method public synchronized void reset();
+ method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
+ method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
+ method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public void reset() throws java.lang.SecurityException;
field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
}
@@ -53680,7 +54877,7 @@
public class Logger {
ctor protected Logger(java.lang.String, java.lang.String);
- method public void addHandler(java.util.logging.Handler);
+ method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void config(java.lang.String);
method public void entering(java.lang.String, java.lang.String);
method public void entering(java.lang.String, java.lang.String, java.lang.Object);
@@ -53693,7 +54890,7 @@
method public static java.util.logging.Logger getAnonymousLogger();
method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
method public java.util.logging.Filter getFilter();
- method public static java.util.logging.Logger getGlobal();
+ method public static final java.util.logging.Logger getGlobal();
method public java.util.logging.Handler[] getHandlers();
method public java.util.logging.Level getLevel();
method public static java.util.logging.Logger getLogger(java.lang.String);
@@ -53705,11 +54902,11 @@
method public boolean getUseParentHandlers();
method public void info(java.lang.String);
method public boolean isLoggable(java.util.logging.Level);
+ method public void log(java.util.logging.LogRecord);
method public void log(java.util.logging.Level, java.lang.String);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
- method public void log(java.util.logging.LogRecord);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
@@ -53718,9 +54915,9 @@
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
- method public void removeHandler(java.util.logging.Handler);
- method public void setFilter(java.util.logging.Filter);
- method public void setLevel(java.util.logging.Level);
+ method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
+ method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
method public void setParent(java.util.logging.Logger);
method public void setUseParentHandlers(boolean);
method public void severe(java.lang.String);
@@ -53737,24 +54934,24 @@
method public abstract void setLoggerLevel(java.lang.String, java.lang.String);
}
- public final class LoggingPermission extends java.security.BasicPermission implements java.security.Guard java.io.Serializable {
- ctor public LoggingPermission(java.lang.String, java.lang.String);
+ public final class LoggingPermission extends java.security.BasicPermission {
+ ctor public LoggingPermission(java.lang.String, java.lang.String) throws java.lang.IllegalArgumentException;
}
public class MemoryHandler extends java.util.logging.Handler {
ctor public MemoryHandler();
ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
- method public void close();
+ method public void close() throws java.lang.SecurityException;
method public void flush();
- method public java.util.logging.Level getPushLevel();
+ method public synchronized java.util.logging.Level getPushLevel();
method public synchronized void publish(java.util.logging.LogRecord);
- method public void push();
- method public void setPushLevel(java.util.logging.Level);
+ method public synchronized void push();
+ method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class SimpleFormatter extends java.util.logging.Formatter {
ctor public SimpleFormatter();
- method public java.lang.String format(java.util.logging.LogRecord);
+ method public synchronized java.lang.String format(java.util.logging.LogRecord);
}
public class SocketHandler extends java.util.logging.StreamHandler {
@@ -53765,10 +54962,10 @@
public class StreamHandler extends java.util.logging.Handler {
ctor public StreamHandler();
ctor public StreamHandler(java.io.OutputStream, java.util.logging.Formatter);
- method public void close();
- method public void flush();
+ method public synchronized void close() throws java.lang.SecurityException;
+ method public synchronized void flush();
method public synchronized void publish(java.util.logging.LogRecord);
- method protected void setOutputStream(java.io.OutputStream);
+ method protected synchronized void setOutputStream(java.io.OutputStream) throws java.lang.SecurityException;
}
public class XMLFormatter extends java.util.logging.Formatter {
@@ -53838,12 +55035,12 @@
}
public class InvalidPreferencesFormatException extends java.lang.Exception {
+ ctor public InvalidPreferencesFormatException(java.lang.Throwable);
ctor public InvalidPreferencesFormatException(java.lang.String);
ctor public InvalidPreferencesFormatException(java.lang.String, java.lang.Throwable);
- ctor public InvalidPreferencesFormatException(java.lang.Throwable);
}
- public class NodeChangeEvent extends java.util.EventObject implements java.io.Serializable {
+ public class NodeChangeEvent extends java.util.EventObject {
ctor public NodeChangeEvent(java.util.prefs.Preferences, java.util.prefs.Preferences);
method public java.util.prefs.Preferences getChild();
method public java.util.prefs.Preferences getParent();
@@ -53854,7 +55051,7 @@
method public abstract void childRemoved(java.util.prefs.NodeChangeEvent);
}
- public class PreferenceChangeEvent extends java.util.EventObject implements java.io.Serializable {
+ public class PreferenceChangeEvent extends java.util.EventObject {
ctor public PreferenceChangeEvent(java.util.prefs.Preferences, java.lang.String, java.lang.String);
method public java.lang.String getKey();
method public java.lang.String getNewValue();
@@ -53999,8 +55196,8 @@
method public long getValue();
method public void reset();
method public void update(int);
- method public void update(byte[]);
method public void update(byte[], int, int);
+ method public void update(byte[]);
}
public class CRC32 implements java.util.zip.Checksum {
@@ -54008,8 +55205,8 @@
method public long getValue();
method public void reset();
method public void update(int);
- method public void update(byte[]);
method public void update(byte[], int, int);
+ method public void update(byte[]);
}
public class CheckedInputStream extends java.io.FilterInputStream {
@@ -54025,8 +55222,8 @@
public abstract interface Checksum {
method public abstract long getValue();
method public abstract void reset();
- method public abstract void update(byte[], int, int);
method public abstract void update(int);
+ method public abstract void update(byte[], int, int);
}
public class DataFormatException extends java.lang.Exception {
@@ -54035,28 +55232,28 @@
}
public class Deflater {
- ctor public Deflater();
- ctor public Deflater(int);
ctor public Deflater(int, boolean);
+ ctor public Deflater(int);
+ ctor public Deflater();
+ method public int deflate(byte[], int, int);
method public int deflate(byte[]);
- method public synchronized int deflate(byte[], int, int);
- method public synchronized int deflate(byte[], int, int, int);
- method public synchronized void end();
- method public synchronized void finish();
- method public synchronized boolean finished();
- method public synchronized int getAdler();
- method public synchronized long getBytesRead();
- method public synchronized long getBytesWritten();
- method public synchronized int getTotalIn();
- method public synchronized int getTotalOut();
- method public synchronized boolean needsInput();
- method public synchronized void reset();
+ method public int deflate(byte[], int, int, int);
+ method public void end();
+ method public void finish();
+ method public boolean finished();
+ method public int getAdler();
+ method public long getBytesRead();
+ method public long getBytesWritten();
+ method public int getTotalIn();
+ method public int getTotalOut();
+ method public boolean needsInput();
+ method public void reset();
+ method public void setDictionary(byte[], int, int);
method public void setDictionary(byte[]);
- method public synchronized void setDictionary(byte[], int, int);
+ method public void setInput(byte[], int, int);
method public void setInput(byte[]);
- method public synchronized void setInput(byte[], int, int);
- method public synchronized void setLevel(int);
- method public synchronized void setStrategy(int);
+ method public void setLevel(int);
+ method public void setStrategy(int);
field public static final int BEST_COMPRESSION = 9; // 0x9
field public static final int BEST_SPEED = 1; // 0x1
field public static final int DEFAULT_COMPRESSION = -1; // 0xffffffff
@@ -54079,12 +55276,12 @@
}
public class DeflaterOutputStream extends java.io.FilterOutputStream {
- ctor public DeflaterOutputStream(java.io.OutputStream);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int);
- ctor public DeflaterOutputStream(java.io.OutputStream, boolean);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, boolean);
ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater);
+ ctor public DeflaterOutputStream(java.io.OutputStream, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream);
method protected void deflate() throws java.io.IOException;
method public void finish() throws java.io.IOException;
field protected byte[] buf;
@@ -54092,49 +55289,50 @@
}
public class GZIPInputStream extends java.util.zip.InflaterInputStream {
- ctor public GZIPInputStream(java.io.InputStream) throws java.io.IOException;
ctor public GZIPInputStream(java.io.InputStream, int) throws java.io.IOException;
+ ctor public GZIPInputStream(java.io.InputStream) throws java.io.IOException;
field public static final int GZIP_MAGIC = 35615; // 0x8b1f
field protected java.util.zip.CRC32 crc;
field protected boolean eos;
}
public class GZIPOutputStream extends java.util.zip.DeflaterOutputStream {
- ctor public GZIPOutputStream(java.io.OutputStream) throws java.io.IOException;
- ctor public GZIPOutputStream(java.io.OutputStream, boolean) throws java.io.IOException;
ctor public GZIPOutputStream(java.io.OutputStream, int) throws java.io.IOException;
ctor public GZIPOutputStream(java.io.OutputStream, int, boolean) throws java.io.IOException;
+ ctor public GZIPOutputStream(java.io.OutputStream) throws java.io.IOException;
+ ctor public GZIPOutputStream(java.io.OutputStream, boolean) throws java.io.IOException;
field protected java.util.zip.CRC32 crc;
}
public class Inflater {
- ctor public Inflater();
ctor public Inflater(boolean);
- method public synchronized void end();
- method public synchronized boolean finished();
- method public synchronized int getAdler();
- method public synchronized long getBytesRead();
- method public synchronized long getBytesWritten();
- method public synchronized int getRemaining();
- method public synchronized int getTotalIn();
- method public synchronized int getTotalOut();
+ ctor public Inflater();
+ method public void end();
+ method public boolean finished();
+ method public int getAdler();
+ method public long getBytesRead();
+ method public long getBytesWritten();
+ method public int getRemaining();
+ method public int getTotalIn();
+ method public int getTotalOut();
+ method public int inflate(byte[], int, int) throws java.util.zip.DataFormatException;
method public int inflate(byte[]) throws java.util.zip.DataFormatException;
- method public synchronized int inflate(byte[], int, int) throws java.util.zip.DataFormatException;
- method public synchronized boolean needsDictionary();
- method public synchronized boolean needsInput();
- method public synchronized void reset();
- method public synchronized void setDictionary(byte[]);
- method public synchronized void setDictionary(byte[], int, int);
- method public synchronized void setInput(byte[]);
- method public synchronized void setInput(byte[], int, int);
+ method public boolean needsDictionary();
+ method public boolean needsInput();
+ method public void reset();
+ method public void setDictionary(byte[], int, int);
+ method public void setDictionary(byte[]);
+ method public void setInput(byte[], int, int);
+ method public void setInput(byte[]);
}
public class InflaterInputStream extends java.io.FilterInputStream {
- ctor public InflaterInputStream(java.io.InputStream);
- ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater);
ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater, int);
+ ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater);
+ ctor public InflaterInputStream(java.io.InputStream);
method protected void fill() throws java.io.IOException;
field protected byte[] buf;
+ field protected boolean closed;
field protected java.util.zip.Inflater inf;
field protected int len;
}
@@ -54222,9 +55420,12 @@
}
public class ZipFile implements java.io.Closeable {
- ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
ctor public ZipFile(java.lang.String) throws java.io.IOException;
ctor public ZipFile(java.io.File, int) throws java.io.IOException;
+ ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
+ ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
+ ctor public ZipFile(java.lang.String, java.nio.charset.Charset) throws java.io.IOException;
+ ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
method public java.lang.String getComment();
@@ -54278,6 +55479,7 @@
public class ZipInputStream extends java.util.zip.InflaterInputStream {
ctor public ZipInputStream(java.io.InputStream);
+ ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -54325,6 +55527,7 @@
public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
ctor public ZipOutputStream(java.io.OutputStream);
+ ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
method public void setComment(java.lang.String);
@@ -54384,8 +55587,8 @@
}
public class BadPaddingException extends java.security.GeneralSecurityException {
- ctor public BadPaddingException(java.lang.String);
ctor public BadPaddingException();
+ ctor public BadPaddingException(java.lang.String);
}
public class Cipher {
@@ -54496,14 +55699,14 @@
method public final int getOutputSize(int) throws java.lang.IllegalStateException;
method public final java.security.Provider getProvider();
method public final void init(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
- method public final void init(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+ method public final void init(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final boolean isCryptoAllowed(java.security.Key) throws javax.crypto.ExemptionMechanismException;
}
public class ExemptionMechanismException extends java.security.GeneralSecurityException {
- ctor public ExemptionMechanismException(java.lang.String);
ctor public ExemptionMechanismException();
+ ctor public ExemptionMechanismException(java.lang.String);
}
public abstract class ExemptionMechanismSpi {
@@ -54512,13 +55715,13 @@
method protected abstract int engineGenExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, javax.crypto.ShortBufferException;
method protected abstract int engineGetOutputSize(int);
method protected abstract void engineInit(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
- method protected abstract void engineInit(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method protected abstract void engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+ method protected abstract void engineInit(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
}
public class IllegalBlockSizeException extends java.security.GeneralSecurityException {
- ctor public IllegalBlockSizeException(java.lang.String);
ctor public IllegalBlockSizeException();
+ ctor public IllegalBlockSizeException(java.lang.String);
}
public class KeyAgreement {
@@ -54556,19 +55759,19 @@
method public static final javax.crypto.KeyGenerator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static final javax.crypto.KeyGenerator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final void init(java.security.SecureRandom);
method public final void init(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
method public final void init(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
method public final void init(int);
method public final void init(int, java.security.SecureRandom);
- method public final void init(java.security.SecureRandom);
}
public abstract class KeyGeneratorSpi {
ctor public KeyGeneratorSpi();
method protected abstract javax.crypto.SecretKey engineGenerateKey();
+ method protected abstract void engineInit(java.security.SecureRandom);
method protected abstract void engineInit(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
method protected abstract void engineInit(int, java.security.SecureRandom);
- method protected abstract void engineInit(java.security.SecureRandom);
}
public class Mac implements java.lang.Cloneable {
@@ -54583,12 +55786,12 @@
method public static final javax.crypto.Mac getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final int getMacLength();
method public final java.security.Provider getProvider();
- method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void init(java.security.Key) throws java.security.InvalidKeyException;
+ method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void reset();
method public final void update(byte) throws java.lang.IllegalStateException;
- method public final void update(byte[], int, int) throws java.lang.IllegalStateException;
method public final void update(byte[]) throws java.lang.IllegalStateException;
+ method public final void update(byte[], int, int) throws java.lang.IllegalStateException;
method public final void update(java.nio.ByteBuffer);
}
@@ -54605,8 +55808,8 @@
}
public class NoSuchPaddingException extends java.security.GeneralSecurityException {
- ctor public NoSuchPaddingException(java.lang.String);
ctor public NoSuchPaddingException();
+ ctor public NoSuchPaddingException(java.lang.String);
}
public class NullCipher extends javax.crypto.Cipher {
@@ -54647,8 +55850,8 @@
}
public class ShortBufferException extends java.security.GeneralSecurityException {
- ctor public ShortBufferException(java.lang.String);
ctor public ShortBufferException();
+ ctor public ShortBufferException(java.lang.String);
}
}
@@ -54793,7 +55996,7 @@
method public int getWordSize();
}
- public class SecretKeySpec implements java.security.spec.KeySpec javax.crypto.SecretKey java.io.Serializable {
+ public class SecretKeySpec implements java.security.spec.KeySpec javax.crypto.SecretKey {
ctor public SecretKeySpec(byte[], java.lang.String);
ctor public SecretKeySpec(byte[], int, int, java.lang.String);
method public java.lang.String getAlgorithm();
@@ -55688,7 +56891,7 @@
method public abstract java.net.ServerSocket createServerSocket(int) throws java.io.IOException;
method public abstract java.net.ServerSocket createServerSocket(int, int) throws java.io.IOException;
method public abstract java.net.ServerSocket createServerSocket(int, int, java.net.InetAddress) throws java.io.IOException;
- method public static synchronized javax.net.ServerSocketFactory getDefault();
+ method public static javax.net.ServerSocketFactory getDefault();
}
public abstract class SocketFactory {
@@ -55698,7 +56901,7 @@
method public abstract java.net.Socket createSocket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException, java.net.UnknownHostException;
method public abstract java.net.Socket createSocket(java.net.InetAddress, int) throws java.io.IOException;
method public abstract java.net.Socket createSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
- method public static synchronized javax.net.SocketFactory getDefault();
+ method public static javax.net.SocketFactory getDefault();
}
}
@@ -55710,6 +56913,12 @@
method public java.security.cert.CertPathParameters getParameters();
}
+ public abstract class ExtendedSSLSession implements javax.net.ssl.SSLSession {
+ ctor public ExtendedSSLSession();
+ method public abstract java.lang.String[] getLocalSupportedSignatureAlgorithms();
+ method public abstract java.lang.String[] getPeerSupportedSignatureAlgorithms();
+ }
+
public class HandshakeCompletedEvent extends java.util.EventObject {
ctor public HandshakeCompletedEvent(javax.net.ssl.SSLSocket, javax.net.ssl.SSLSession);
method public java.lang.String getCipherSuite();
@@ -55785,7 +56994,7 @@
method public final javax.net.ssl.SSLEngine createSSLEngine();
method public final javax.net.ssl.SSLEngine createSSLEngine(java.lang.String, int);
method public final javax.net.ssl.SSLSessionContext getClientSessionContext();
- method public static javax.net.ssl.SSLContext getDefault() throws java.security.NoSuchAlgorithmException;
+ method public static synchronized javax.net.ssl.SSLContext getDefault() throws java.security.NoSuchAlgorithmException;
method public final javax.net.ssl.SSLParameters getDefaultSSLParameters();
method public static javax.net.ssl.SSLContext getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
method public static javax.net.ssl.SSLContext getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
@@ -55797,13 +57006,13 @@
method public final javax.net.ssl.SSLSocketFactory getSocketFactory();
method public final javax.net.ssl.SSLParameters getSupportedSSLParameters();
method public final void init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) throws java.security.KeyManagementException;
- method public static void setDefault(javax.net.ssl.SSLContext);
+ method public static synchronized void setDefault(javax.net.ssl.SSLContext);
}
public abstract class SSLContextSpi {
ctor public SSLContextSpi();
- method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine(java.lang.String, int);
method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine();
+ method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine(java.lang.String, int);
method protected abstract javax.net.ssl.SSLSessionContext engineGetClientSessionContext();
method protected javax.net.ssl.SSLParameters engineGetDefaultSSLParameters();
method protected abstract javax.net.ssl.SSLSessionContext engineGetServerSessionContext();
@@ -55823,6 +57032,7 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
+ method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
method public abstract boolean getNeedClientAuth();
method public java.lang.String getPeerHost();
@@ -55842,12 +57052,12 @@
method public void setSSLParameters(javax.net.ssl.SSLParameters);
method public abstract void setUseClientMode(boolean);
method public abstract void setWantClientAuth(boolean);
- method public abstract javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[], int, int) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[]) throws javax.net.ssl.SSLException;
- method public abstract javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], int, int, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
- method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public abstract javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[], int, int) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public abstract javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], int, int, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
}
public class SSLEngineResult {
@@ -55895,11 +57105,15 @@
ctor public SSLParameters();
ctor public SSLParameters(java.lang.String[]);
ctor public SSLParameters(java.lang.String[], java.lang.String[]);
+ method public java.security.AlgorithmConstraints getAlgorithmConstraints();
method public java.lang.String[] getCipherSuites();
+ method public java.lang.String getEndpointIdentificationAlgorithm();
method public boolean getNeedClientAuth();
method public java.lang.String[] getProtocols();
method public boolean getWantClientAuth();
+ method public void setAlgorithmConstraints(java.security.AlgorithmConstraints);
method public void setCipherSuites(java.lang.String[]);
+ method public void setEndpointIdentificationAlgorithm(java.lang.String);
method public void setNeedClientAuth(boolean);
method public void setProtocols(java.lang.String[]);
method public void setWantClientAuth(boolean);
@@ -55927,6 +57141,7 @@
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
method public abstract boolean getNeedClientAuth();
+ method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract java.lang.String[] getSupportedCipherSuites();
method public abstract java.lang.String[] getSupportedProtocols();
method public abstract boolean getUseClientMode();
@@ -55935,6 +57150,7 @@
method public abstract void setEnabledCipherSuites(java.lang.String[]);
method public abstract void setEnabledProtocols(java.lang.String[]);
method public abstract void setNeedClientAuth(boolean);
+ method public void setSSLParameters(javax.net.ssl.SSLParameters);
method public abstract void setUseClientMode(boolean);
method public abstract void setWantClientAuth(boolean);
}
@@ -56000,6 +57216,7 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
+ method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract boolean getNeedClientAuth();
method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract javax.net.ssl.SSLSession getSession();
@@ -56055,6 +57272,14 @@
method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine);
}
+ public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager {
+ ctor public X509ExtendedTrustManager();
+ method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
+ method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+ method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
+ method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+ }
+
public abstract interface X509KeyManager implements javax.net.ssl.KeyManager {
method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket);
method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket);
@@ -56089,11 +57314,21 @@
method public abstract boolean isDestroyed();
}
+ public abstract deprecated class Policy {
+ ctor protected Policy();
+ method public abstract java.security.PermissionCollection getPermissions(javax.security.auth.Subject, java.security.CodeSource);
+ method public static javax.security.auth.Policy getPolicy();
+ method public abstract void refresh();
+ method public static void setPolicy(javax.security.auth.Policy);
+ }
+
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -56151,6 +57386,43 @@
package javax.security.auth.login {
+ public class AppConfigurationEntry {
+ ctor public AppConfigurationEntry(java.lang.String, javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag, java.util.Map<java.lang.String, ?>);
+ method public javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag getControlFlag();
+ method public java.lang.String getLoginModuleName();
+ method public java.util.Map<java.lang.String, ?> getOptions();
+ }
+
+ public static class AppConfigurationEntry.LoginModuleControlFlag {
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag OPTIONAL;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag REQUIRED;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag REQUISITE;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag SUFFICIENT;
+ }
+
+ public abstract class Configuration {
+ ctor protected Configuration();
+ method public abstract javax.security.auth.login.AppConfigurationEntry[] getAppConfigurationEntry(java.lang.String);
+ method public static javax.security.auth.login.Configuration getConfiguration();
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters) throws java.security.NoSuchAlgorithmException;
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+ method public javax.security.auth.login.Configuration.Parameters getParameters();
+ method public java.security.Provider getProvider();
+ method public java.lang.String getType();
+ method public void refresh();
+ method public static void setConfiguration(javax.security.auth.login.Configuration);
+ }
+
+ public static abstract interface Configuration.Parameters {
+ }
+
+ public abstract class ConfigurationSpi {
+ ctor public ConfigurationSpi();
+ method protected abstract javax.security.auth.login.AppConfigurationEntry[] engineGetAppConfigurationEntry(java.lang.String);
+ method protected void engineRefresh();
+ }
+
public class LoginException extends java.security.GeneralSecurityException {
ctor public LoginException();
ctor public LoginException(java.lang.String);
@@ -56161,10 +57433,10 @@
package javax.security.auth.x500 {
public final class X500Principal implements java.security.Principal java.io.Serializable {
- ctor public X500Principal(byte[]);
- ctor public X500Principal(java.io.InputStream);
ctor public X500Principal(java.lang.String);
ctor public X500Principal(java.lang.String, java.util.Map<java.lang.String, java.lang.String>);
+ ctor public X500Principal(byte[]);
+ ctor public X500Principal(java.io.InputStream);
method public byte[] getEncoded();
method public java.lang.String getName();
method public java.lang.String getName(java.lang.String);
@@ -56188,28 +57460,28 @@
}
public class CertificateEncodingException extends javax.security.cert.CertificateException {
- ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException();
+ ctor public CertificateEncodingException(java.lang.String);
}
public class CertificateException extends java.lang.Exception {
- ctor public CertificateException(java.lang.String);
ctor public CertificateException();
+ ctor public CertificateException(java.lang.String);
}
public class CertificateExpiredException extends javax.security.cert.CertificateException {
- ctor public CertificateExpiredException(java.lang.String);
ctor public CertificateExpiredException();
+ ctor public CertificateExpiredException(java.lang.String);
}
public class CertificateNotYetValidException extends javax.security.cert.CertificateException {
- ctor public CertificateNotYetValidException(java.lang.String);
ctor public CertificateNotYetValidException();
+ ctor public CertificateNotYetValidException(java.lang.String);
}
public class CertificateParsingException extends javax.security.cert.CertificateException {
- ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException();
+ ctor public CertificateParsingException(java.lang.String);
}
public abstract class X509Certificate extends javax.security.cert.Certificate {
@@ -56236,11 +57508,12 @@
public abstract interface CommonDataSource {
method public abstract java.io.PrintWriter getLogWriter() throws java.sql.SQLException;
method public abstract int getLoginTimeout() throws java.sql.SQLException;
+ method public abstract java.util.logging.Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException;
method public abstract void setLogWriter(java.io.PrintWriter) throws java.sql.SQLException;
method public abstract void setLoginTimeout(int) throws java.sql.SQLException;
}
- public class ConnectionEvent extends java.util.EventObject implements java.io.Serializable {
+ public class ConnectionEvent extends java.util.EventObject {
ctor public ConnectionEvent(javax.sql.PooledConnection);
ctor public ConnectionEvent(javax.sql.PooledConnection, java.sql.SQLException);
method public java.sql.SQLException getSQLException();
@@ -56289,21 +57562,21 @@
method public abstract void removeRowSetListener(javax.sql.RowSetListener);
method public abstract void setArray(int, java.sql.Array) throws java.sql.SQLException;
method public abstract void setAsciiStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+ method public abstract void setAsciiStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setAsciiStream(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setAsciiStream(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
- method public abstract void setAsciiStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBigDecimal(int, java.math.BigDecimal) throws java.sql.SQLException;
method public abstract void setBigDecimal(java.lang.String, java.math.BigDecimal) throws java.sql.SQLException;
method public abstract void setBinaryStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+ method public abstract void setBinaryStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBinaryStream(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBinaryStream(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
- method public abstract void setBinaryStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBlob(int, java.sql.Blob) throws java.sql.SQLException;
- method public abstract void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBlob(int, java.io.InputStream, long) throws java.sql.SQLException;
- method public abstract void setBlob(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
+ method public abstract void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBlob(java.lang.String, java.io.InputStream, long) throws java.sql.SQLException;
method public abstract void setBlob(java.lang.String, java.sql.Blob) throws java.sql.SQLException;
+ method public abstract void setBlob(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBoolean(int, boolean) throws java.sql.SQLException;
method public abstract void setBoolean(java.lang.String, boolean) throws java.sql.SQLException;
method public abstract void setByte(int, byte) throws java.sql.SQLException;
@@ -56311,15 +57584,15 @@
method public abstract void setBytes(int, byte[]) throws java.sql.SQLException;
method public abstract void setBytes(java.lang.String, byte[]) throws java.sql.SQLException;
method public abstract void setCharacterStream(int, java.io.Reader, int) throws java.sql.SQLException;
+ method public abstract void setCharacterStream(java.lang.String, java.io.Reader, int) throws java.sql.SQLException;
method public abstract void setCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setCharacterStream(java.lang.String, java.io.Reader, int) throws java.sql.SQLException;
method public abstract void setClob(int, java.sql.Clob) throws java.sql.SQLException;
- method public abstract void setClob(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setClob(int, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setClob(int, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setClob(java.lang.String, java.sql.Clob) throws java.sql.SQLException;
method public abstract void setClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setCommand(java.lang.String) throws java.sql.SQLException;
method public abstract void setConcurrency(int) throws java.sql.SQLException;
method public abstract void setDataSourceName(java.lang.String) throws java.sql.SQLException;
@@ -56340,26 +57613,26 @@
method public abstract void setMaxRows(int) throws java.sql.SQLException;
method public abstract void setNCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setNCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
- method public abstract void setNCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
method public abstract void setNCharacterStream(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setNCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.sql.NClob) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setNClob(int, java.sql.NClob) throws java.sql.SQLException;
method public abstract void setNClob(int, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.sql.NClob) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setNString(int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(int, int) throws java.sql.SQLException;
- method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int, int) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(java.lang.String, java.lang.Object, int, int) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setPassword(java.lang.String) throws java.sql.SQLException;
method public abstract void setQueryTimeout(int) throws java.sql.SQLException;
method public abstract void setReadOnly(boolean) throws java.sql.SQLException;
@@ -56377,8 +57650,8 @@
method public abstract void setTime(java.lang.String, java.sql.Time) throws java.sql.SQLException;
method public abstract void setTime(java.lang.String, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(int, java.sql.Timestamp) throws java.sql.SQLException;
- method public abstract void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(java.lang.String, java.sql.Timestamp) throws java.sql.SQLException;
+ method public abstract void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(java.lang.String, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTransactionIsolation(int) throws java.sql.SQLException;
method public abstract void setType(int) throws java.sql.SQLException;
@@ -56388,7 +57661,7 @@
method public abstract void setUsername(java.lang.String) throws java.sql.SQLException;
}
- public class RowSetEvent extends java.util.EventObject implements java.io.Serializable {
+ public class RowSetEvent extends java.util.EventObject {
ctor public RowSetEvent(javax.sql.RowSet);
}
@@ -56435,8 +57708,8 @@
}
public class StatementEvent extends java.util.EventObject {
- ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement, java.sql.SQLException);
ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement);
+ ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement, java.sql.SQLException);
method public java.sql.SQLException getSQLException();
method public java.sql.PreparedStatement getStatement();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 8729d84..fbc60cd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -43,6 +43,7 @@
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
+ field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
@@ -424,6 +425,7 @@
field public static final int calendarTextColor = 16843931; // 0x101049b
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
+ field public static final int canControlMagnification = 16844040; // 0x1010508
field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -591,6 +593,7 @@
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
+ field public static final int encryptionAware = 16844038; // 0x1010506
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
field public static final deprecated int endYear = 16843133; // 0x101017d
@@ -651,6 +654,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceDeviceEncrypted = 16844037; // 0x1010505
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -1023,6 +1027,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1804,11 +1809,13 @@
field public static final int icon = 16908294; // 0x1020006
field public static final int icon1 = 16908295; // 0x1020007
field public static final int icon2 = 16908296; // 0x1020008
+ field public static final int icon_frame = 16908350; // 0x102003e
field public static final int input = 16908297; // 0x1020009
field public static final int inputArea = 16908318; // 0x102001e
field public static final int inputExtractEditText = 16908325; // 0x1020025
field public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
+ field public static final int list_container = 16908351; // 0x102003f
field public static final int mask = 16908334; // 0x102002e
field public static final int message = 16908299; // 0x102000b
field public static final int navigationBarBackground = 16908336; // 0x1020030
@@ -1828,6 +1835,7 @@
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
+ field public static final int switch_widget = 16908352; // 0x1020040
field public static final int tabcontent = 16908305; // 0x1020011
field public static final int tabhost = 16908306; // 0x1020012
field public static final int tabs = 16908307; // 0x1020013
@@ -2712,6 +2720,7 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+ method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
@@ -2749,6 +2758,23 @@
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
}
+ public static final class AccessibilityService.MagnificationController {
+ method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
+ method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
+ method public float getCenterX();
+ method public float getCenterY();
+ method public android.graphics.Region getMagnifiedRegion();
+ method public float getScale();
+ method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
+ method public boolean reset(boolean);
+ method public boolean setCenter(float, float, boolean);
+ method public boolean setScale(float, boolean);
+ }
+
+ public static abstract interface AccessibilityService.MagnificationController.OnMagnificationChangedListener {
+ method public abstract void onMagnificationChanged(android.accessibilityservice.AccessibilityService.MagnificationController, android.graphics.Region, float, float, float);
+ }
+
public class AccessibilityServiceInfo implements android.os.Parcelable {
ctor public AccessibilityServiceInfo();
method public static java.lang.String capabilityToString(int);
@@ -2763,6 +2789,7 @@
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2805,6 +2832,8 @@
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2869,6 +2898,8 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2885,6 +2916,8 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
+ field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+ field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3572,6 +3605,7 @@
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
+ method public void overlayWithDecorCaption(boolean);
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
@@ -3856,6 +3890,8 @@
}
public class ActivityOptions {
+ method public android.graphics.Rect getLaunchBounds();
+ method public boolean hasLaunchBounds();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3865,6 +3901,7 @@
method public static android.app.ActivityOptions makeTaskLaunchBehind();
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public void requestUsageTimeReport(android.app.PendingIntent);
+ method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
@@ -4887,7 +4924,7 @@
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
- method public android.app.Notification.Topic[] getTopics();
+ method public android.app.Notification.Topic getTopic();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -4950,6 +4987,7 @@
field public static final int PRIORITY_MAX = 2; // 0x2
field public static final int PRIORITY_MIN = -2; // 0xfffffffe
field public static final deprecated int STREAM_DEFAULT = -1; // 0xffffffff
+ field public static final java.lang.String TOPIC_DEFAULT = "system_default_topic";
field public static final int VISIBILITY_PRIVATE = 0; // 0x0
field public static final int VISIBILITY_PUBLIC = 1; // 0x1
field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
@@ -5052,7 +5090,6 @@
method public android.app.Notification.Builder addAction(android.app.Notification.Action);
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
- method public android.app.Notification.Builder addTopic(android.app.Notification.Topic);
method public android.app.Notification build();
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
@@ -5101,6 +5138,7 @@
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+ method public android.app.Notification.Builder setTopic(android.app.Notification.Topic);
method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setVisibility(int);
@@ -5258,10 +5296,12 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public NotificationManager.Policy(int, int, int);
+ ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
method public static java.lang.String prioritySendersToString(int);
+ method public static java.lang.String suppressedEffectsToString(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
@@ -5272,9 +5312,13 @@
field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0
field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1
field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2
+ field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff
+ field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
+ field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
field public final int priorityCallSenders;
field public final int priorityCategories;
field public final int priorityMessageSenders;
+ field public final int suppressedVisualEffects;
}
public final class PendingIntent implements android.os.Parcelable {
@@ -5831,6 +5875,7 @@
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public java.lang.String getDeviceOwner();
+ method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5861,6 +5906,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
+ method public java.lang.String getWifiMacAddress();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5889,6 +5935,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -5956,6 +6003,8 @@
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+ field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
+ field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
@@ -8481,6 +8530,7 @@
field public static final java.lang.String ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED";
field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
+ field public static final java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
@@ -8528,6 +8578,7 @@
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
field public static final java.lang.String ACTION_REBOOT = "android.intent.action.REBOOT";
+ field public static final java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
field public static final java.lang.String ACTION_RUN = "android.intent.action.RUN";
field public static final java.lang.String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
field public static final java.lang.String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
@@ -10253,6 +10304,7 @@
public static class Resources.NotFoundException extends java.lang.RuntimeException {
ctor public Resources.NotFoundException();
ctor public Resources.NotFoundException(java.lang.String);
+ ctor public Resources.NotFoundException(java.lang.String, java.lang.Exception);
}
public final class Resources.Theme {
@@ -13763,6 +13815,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_TIMESTAMP_SOURCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_WHITE_LEVEL;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_MAX_ANALOG_SENSITIVITY;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect[]> SENSOR_OPTICAL_BLACK_REGIONS;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_ORIENTATION;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_REFERENCE_ILLUMINANT1;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> SENSOR_REFERENCE_ILLUMINANT2;
@@ -14175,6 +14228,8 @@
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
+ field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_DYNAMIC_BLACK_LEVEL;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> SENSOR_DYNAMIC_WHITE_LEVEL;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_FRAME_DURATION;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> SENSOR_GREEN_SPLIT;
@@ -21486,10 +21541,13 @@
method public void writeToParcel(android.os.Parcel, int);
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field public int exponent;
+ field public int maxPeriodInMs;
field public int maxScansToCache;
field public int numBssidsPerScan;
field public int periodInMs;
field public int reportEvents;
+ field public int stepCount;
}
public static abstract interface WifiScanner.WifiChangeListener implements android.net.wifi.WifiScanner.ActionListener {
@@ -27729,6 +27787,7 @@
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String CONTENT_VCARD_TYPE = "text/x-vcard";
field public static final android.net.Uri CONTENT_VCARD_URI;
+ field public static final android.net.Uri CORP_CONTENT_FILTER_URI;
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
@@ -27849,12 +27908,15 @@
}
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
+ method public static boolean isEnterpriseDirectoryId(long);
+ method public static boolean isRemoteDirectory(long);
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
field public static final java.lang.String ACCOUNT_TYPE = "accountType";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
field public static final android.net.Uri CONTENT_URI;
+ field public static final android.net.Uri CORP_CONTENT_URI;
field public static final long DEFAULT = 0L; // 0x0L
field public static final java.lang.String DIRECTORY_AUTHORITY = "authority";
field public static final java.lang.String DISPLAY_NAME = "displayName";
@@ -28244,6 +28306,7 @@
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
@@ -31081,6 +31144,8 @@
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
+ field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
+ field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
field public static final int TRIM_FULL = 0; // 0x0
field public static final int TRIM_LIGHT = 1; // 0x1
}
@@ -31089,6 +31154,7 @@
ctor public NotificationListenerService.Ranking();
method public java.lang.String getKey();
method public int getRank();
+ method public int getSuppressedVisualEffects();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
}
@@ -31148,6 +31214,35 @@
}
+package android.service.quicksettings {
+
+ public final class Tile implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.CharSequence getContentDescription();
+ method public android.graphics.drawable.Icon getIcon();
+ method public java.lang.CharSequence getLabel();
+ method public void setContentDescription(java.lang.CharSequence);
+ method public void setIcon(android.graphics.drawable.Icon);
+ method public void setLabel(java.lang.CharSequence);
+ method public void updateTile();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+ }
+
+ public class TileService extends android.app.Service {
+ ctor public TileService();
+ method public final android.service.quicksettings.Tile getQsTile();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public void onClick();
+ method public void onStartListening();
+ method public void onStopListening();
+ method public void onTileAdded();
+ method public void onTileRemoved();
+ field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+ }
+
+}
+
package android.service.restrictions {
public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver {
@@ -32960,6 +33055,7 @@
public class TelecomManager {
method public void acceptRingingCall();
+ method public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void addNewUnknownCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void cancelMissedCallsNotification();
@@ -36687,6 +36783,7 @@
ctor public LocaleList(java.util.Locale[]);
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public java.util.Locale getBestMatch(java.lang.String[]);
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getPrimary();
@@ -38518,6 +38615,7 @@
method public boolean canResolveTextDirection();
method public boolean canScrollHorizontally(int);
method public boolean canScrollVertically(int);
+ method public final void cancelDragAndDrop();
method public void cancelLongPress();
method public final void cancelPendingInputEvents();
method public boolean checkInputConnectionProxy(android.view.View);
@@ -38535,6 +38633,7 @@
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
+ method public final boolean didLayoutParamsChange();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
method public void dispatchDisplayHint(int);
@@ -38760,6 +38859,7 @@
method public boolean isOpaque();
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
+ method public final boolean isPartialLayoutRequested();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -38996,11 +39096,13 @@
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int);
method public void startAnimation(android.view.animation.Animation);
- method public final boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final deprecated boolean startDrag(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
+ method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
method public boolean startNestedScroll(int);
method public void stopNestedScroll();
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable);
+ method public final void updateDragShadow(android.view.View.DragShadowBuilder);
method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
method public boolean willNotCacheDrawing();
method public boolean willNotDraw();
@@ -39369,6 +39471,7 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
+ method public int findDependentLayoutAxes(android.view.View, int);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -39435,6 +39538,8 @@
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
+ method public void requestLayoutForChild(android.view.View);
+ method public void requestPartialLayoutForChild(android.view.View);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public void scheduleLayoutAnimation();
@@ -39549,6 +39654,7 @@
method public abstract void childHasTransientStateChanged(android.view.View, boolean);
method public abstract void clearChildFocus(android.view.View);
method public abstract void createContextMenu(android.view.ContextMenu);
+ method public abstract int findDependentLayoutAxes(android.view.View, int);
method public abstract android.view.View focusSearch(android.view.View, int);
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -39578,12 +39684,16 @@
method public abstract void requestDisallowInterceptTouchEvent(boolean);
method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
+ method public abstract void requestLayoutForChild(android.view.View);
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
+ field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
+ field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
+ field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
}
public class ViewPropertyAnimator {
@@ -45291,13 +45401,18 @@
package java.awt.font {
public final class NumericShaper implements java.io.Serializable {
- method public static java.awt.font.NumericShaper getContextualShaper(int, int);
method public static java.awt.font.NumericShaper getContextualShaper(int);
+ method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>);
+ method public static java.awt.font.NumericShaper getContextualShaper(int, int);
+ method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>, java.awt.font.NumericShaper.Range);
+ method public java.util.Set<java.awt.font.NumericShaper.Range> getRangeSet();
method public int getRanges();
method public static java.awt.font.NumericShaper getShaper(int);
+ method public static java.awt.font.NumericShaper getShaper(java.awt.font.NumericShaper.Range);
method public boolean isContextual();
- method public void shape(char[], int, int, int);
method public void shape(char[], int, int);
+ method public void shape(char[], int, int, int);
+ method public void shape(char[], int, int, java.awt.font.NumericShaper.Range);
field public static final int ALL_RANGES = 524287; // 0x7ffff
field public static final int ARABIC = 2; // 0x2
field public static final int BENGALI = 16; // 0x10
@@ -45320,6 +45435,46 @@
field public static final int TIBETAN = 16384; // 0x4000
}
+ public static class NumericShaper.Range extends java.lang.Enum {
+ method public static java.awt.font.NumericShaper.Range valueOf(java.lang.String);
+ method public static final java.awt.font.NumericShaper.Range[] values();
+ enum_constant public static final java.awt.font.NumericShaper.Range ARABIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range BALINESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range BENGALI;
+ enum_constant public static final java.awt.font.NumericShaper.Range CHAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range DEVANAGARI;
+ enum_constant public static final java.awt.font.NumericShaper.Range EASTERN_ARABIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range ETHIOPIC;
+ enum_constant public static final java.awt.font.NumericShaper.Range EUROPEAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range GUJARATI;
+ enum_constant public static final java.awt.font.NumericShaper.Range GURMUKHI;
+ enum_constant public static final java.awt.font.NumericShaper.Range JAVANESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range KANNADA;
+ enum_constant public static final java.awt.font.NumericShaper.Range KAYAH_LI;
+ enum_constant public static final java.awt.font.NumericShaper.Range KHMER;
+ enum_constant public static final java.awt.font.NumericShaper.Range LAO;
+ enum_constant public static final java.awt.font.NumericShaper.Range LEPCHA;
+ enum_constant public static final java.awt.font.NumericShaper.Range LIMBU;
+ enum_constant public static final java.awt.font.NumericShaper.Range MALAYALAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range MEETEI_MAYEK;
+ enum_constant public static final java.awt.font.NumericShaper.Range MONGOLIAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR;
+ enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR_SHAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range NEW_TAI_LUE;
+ enum_constant public static final java.awt.font.NumericShaper.Range NKO;
+ enum_constant public static final java.awt.font.NumericShaper.Range OL_CHIKI;
+ enum_constant public static final java.awt.font.NumericShaper.Range ORIYA;
+ enum_constant public static final java.awt.font.NumericShaper.Range SAURASHTRA;
+ enum_constant public static final java.awt.font.NumericShaper.Range SUNDANESE;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_HORA;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_THAM;
+ enum_constant public static final java.awt.font.NumericShaper.Range TAMIL;
+ enum_constant public static final java.awt.font.NumericShaper.Range TELUGU;
+ enum_constant public static final java.awt.font.NumericShaper.Range THAI;
+ enum_constant public static final java.awt.font.NumericShaper.Range TIBETAN;
+ enum_constant public static final java.awt.font.NumericShaper.Range VAI;
+ }
+
public final class TextAttribute extends java.text.AttributedCharacterIterator.Attribute {
ctor protected TextAttribute(java.lang.String);
field public static final java.awt.font.TextAttribute BACKGROUND;
@@ -45413,20 +45568,20 @@
public class PropertyChangeSupport implements java.io.Serializable {
ctor public PropertyChangeSupport(java.lang.Object);
- method public void addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void addPropertyChangeListener(java.beans.PropertyChangeListener);
+ method public void addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void fireIndexedPropertyChange(java.lang.String, int, java.lang.Object, java.lang.Object);
- method public void fireIndexedPropertyChange(java.lang.String, int, boolean, boolean);
method public void fireIndexedPropertyChange(java.lang.String, int, int, int);
+ method public void fireIndexedPropertyChange(java.lang.String, int, boolean, boolean);
method public void firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object);
- method public void firePropertyChange(java.lang.String, boolean, boolean);
method public void firePropertyChange(java.lang.String, int, int);
+ method public void firePropertyChange(java.lang.String, boolean, boolean);
method public void firePropertyChange(java.beans.PropertyChangeEvent);
- method public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String);
method public java.beans.PropertyChangeListener[] getPropertyChangeListeners();
+ method public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String);
method public boolean hasListeners(java.lang.String);
- method public void removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
method public void removePropertyChangeListener(java.beans.PropertyChangeListener);
+ method public void removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener);
}
}
@@ -45451,8 +45606,8 @@
}
public class BufferedReader extends java.io.Reader {
- ctor public BufferedReader(java.io.Reader);
ctor public BufferedReader(java.io.Reader, int);
+ ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
@@ -45481,10 +45636,10 @@
ctor public ByteArrayOutputStream();
ctor public ByteArrayOutputStream(int);
method public synchronized void reset();
- method public int size();
+ method public synchronized int size();
method public synchronized byte[] toByteArray();
- method public deprecated java.lang.String toString(int);
- method public java.lang.String toString(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public synchronized java.lang.String toString(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public deprecated synchronized java.lang.String toString(int);
method public synchronized void write(int);
method public synchronized void writeTo(java.io.OutputStream) throws java.io.IOException;
field protected byte[] buf;
@@ -45526,13 +45681,15 @@
}
public final class Console implements java.io.Flushable {
+ method public static java.io.Console console();
method public void flush();
method public java.io.Console format(java.lang.String, java.lang.Object...);
+ method public static synchronized java.io.Console getConsole();
method public java.io.Console printf(java.lang.String, java.lang.Object...);
- method public java.lang.String readLine();
method public java.lang.String readLine(java.lang.String, java.lang.Object...);
- method public char[] readPassword();
+ method public java.lang.String readLine();
method public char[] readPassword(java.lang.String, java.lang.Object...);
+ method public char[] readPassword();
method public java.io.Reader reader();
method public java.io.PrintWriter writer();
}
@@ -45578,9 +45735,9 @@
}
public abstract interface DataOutput {
+ method public abstract void write(int) throws java.io.IOException;
method public abstract void write(byte[]) throws java.io.IOException;
method public abstract void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
method public abstract void writeBoolean(boolean) throws java.io.IOException;
method public abstract void writeByte(int) throws java.io.IOException;
method public abstract void writeBytes(java.lang.String) throws java.io.IOException;
@@ -45622,17 +45779,17 @@
}
public class File implements java.lang.Comparable java.io.Serializable {
- ctor public File(java.io.File, java.lang.String);
ctor public File(java.lang.String);
ctor public File(java.lang.String, java.lang.String);
+ ctor public File(java.io.File, java.lang.String);
ctor public File(java.net.URI);
method public boolean canExecute();
method public boolean canRead();
method public boolean canWrite();
method public int compareTo(java.io.File);
method public boolean createNewFile() throws java.io.IOException;
- method public static java.io.File createTempFile(java.lang.String, java.lang.String) throws java.io.IOException;
method public static java.io.File createTempFile(java.lang.String, java.lang.String, java.io.File) throws java.io.IOException;
+ method public static java.io.File createTempFile(java.lang.String, java.lang.String) throws java.io.IOException;
method public boolean delete();
method public void deleteOnExit();
method public boolean exists();
@@ -45670,6 +45827,7 @@
method public boolean setReadable(boolean);
method public boolean setWritable(boolean, boolean);
method public boolean setWritable(boolean);
+ method public java.nio.file.Path toPath();
method public java.net.URI toURI();
method public deprecated java.net.URL toURL() throws java.net.MalformedURLException;
field public static final java.lang.String pathSeparator;
@@ -45692,9 +45850,9 @@
}
public class FileInputStream extends java.io.InputStream {
+ ctor public FileInputStream(java.lang.String) throws java.io.FileNotFoundException;
ctor public FileInputStream(java.io.File) throws java.io.FileNotFoundException;
ctor public FileInputStream(java.io.FileDescriptor);
- ctor public FileInputStream(java.lang.String) throws java.io.FileNotFoundException;
method public java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public int read() throws java.io.IOException;
@@ -45706,11 +45864,11 @@
}
public class FileOutputStream extends java.io.OutputStream {
+ ctor public FileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
+ ctor public FileOutputStream(java.lang.String, boolean) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.File) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.File, boolean) throws java.io.FileNotFoundException;
ctor public FileOutputStream(java.io.FileDescriptor);
- ctor public FileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
- ctor public FileOutputStream(java.lang.String, boolean) throws java.io.FileNotFoundException;
method public java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public void write(int) throws java.io.IOException;
@@ -45718,22 +45876,24 @@
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
public class FileReader extends java.io.InputStreamReader {
+ ctor public FileReader(java.lang.String) throws java.io.FileNotFoundException;
ctor public FileReader(java.io.File) throws java.io.FileNotFoundException;
ctor public FileReader(java.io.FileDescriptor);
- ctor public FileReader(java.lang.String) throws java.io.FileNotFoundException;
}
public class FileWriter extends java.io.OutputStreamWriter {
+ ctor public FileWriter(java.lang.String) throws java.io.IOException;
+ ctor public FileWriter(java.lang.String, boolean) throws java.io.IOException;
ctor public FileWriter(java.io.File) throws java.io.IOException;
ctor public FileWriter(java.io.File, boolean) throws java.io.IOException;
ctor public FileWriter(java.io.FileDescriptor);
- ctor public FileWriter(java.lang.String) throws java.io.IOException;
- ctor public FileWriter(java.lang.String, boolean) throws java.io.IOException;
}
public abstract interface FilenameFilter {
@@ -45786,7 +45946,7 @@
ctor public InputStream();
method public int available() throws java.io.IOException;
method public void close() throws java.io.IOException;
- method public void mark(int);
+ method public synchronized void mark(int);
method public boolean markSupported();
method public abstract int read() throws java.io.IOException;
method public int read(byte[]) throws java.io.IOException;
@@ -45798,8 +45958,8 @@
public class InputStreamReader extends java.io.Reader {
ctor public InputStreamReader(java.io.InputStream);
ctor public InputStreamReader(java.io.InputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public InputStreamReader(java.io.InputStream, java.nio.charset.CharsetDecoder);
ctor public InputStreamReader(java.io.InputStream, java.nio.charset.Charset);
+ ctor public InputStreamReader(java.io.InputStream, java.nio.charset.CharsetDecoder);
method public void close() throws java.io.IOException;
method public java.lang.String getEncoding();
method public int read(char[], int, int) throws java.io.IOException;
@@ -45808,6 +45968,7 @@
public class InterruptedIOException extends java.io.IOException {
ctor public InterruptedIOException();
ctor public InterruptedIOException(java.lang.String);
+ ctor public InterruptedIOException(java.lang.Throwable);
field public int bytesTransferred;
}
@@ -45835,13 +45996,13 @@
}
public class NotActiveException extends java.io.ObjectStreamException {
- ctor public NotActiveException();
ctor public NotActiveException(java.lang.String);
+ ctor public NotActiveException();
}
public class NotSerializableException extends java.io.ObjectStreamException {
- ctor public NotSerializableException();
ctor public NotSerializableException(java.lang.String);
+ ctor public NotSerializableException();
}
public abstract interface ObjectInput implements java.lang.AutoCloseable java.io.DataInput {
@@ -45855,32 +46016,32 @@
}
public class ObjectInputStream extends java.io.InputStream implements java.io.ObjectInput java.io.ObjectStreamConstants {
- ctor protected ObjectInputStream() throws java.io.IOException;
- ctor public ObjectInputStream(java.io.InputStream) throws java.io.IOException, java.io.StreamCorruptedException;
- method public void defaultReadObject() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.NotActiveException;
- method protected boolean enableResolveObject(boolean);
+ ctor public ObjectInputStream(java.io.InputStream) throws java.io.IOException;
+ ctor protected ObjectInputStream() throws java.io.IOException, java.lang.SecurityException;
+ method public void defaultReadObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+ method protected boolean enableResolveObject(boolean) throws java.lang.SecurityException;
method public int read() throws java.io.IOException;
method public boolean readBoolean() throws java.io.IOException;
method public byte readByte() throws java.io.IOException;
method public char readChar() throws java.io.IOException;
method protected java.io.ObjectStreamClass readClassDescriptor() throws java.lang.ClassNotFoundException, java.io.IOException;
method public double readDouble() throws java.io.IOException;
- method public java.io.ObjectInputStream.GetField readFields() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.NotActiveException;
+ method public java.io.ObjectInputStream.GetField readFields() throws java.lang.ClassNotFoundException, java.io.IOException;
method public float readFloat() throws java.io.IOException;
method public void readFully(byte[]) throws java.io.IOException;
method public void readFully(byte[], int, int) throws java.io.IOException;
method public int readInt() throws java.io.IOException;
method public deprecated java.lang.String readLine() throws java.io.IOException;
method public long readLong() throws java.io.IOException;
- method public final java.lang.Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.OptionalDataException;
- method protected java.lang.Object readObjectOverride() throws java.lang.ClassNotFoundException, java.io.IOException, java.io.OptionalDataException;
+ method public final java.lang.Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+ method protected java.lang.Object readObjectOverride() throws java.lang.ClassNotFoundException, java.io.IOException;
method public short readShort() throws java.io.IOException;
method protected void readStreamHeader() throws java.io.IOException, java.io.StreamCorruptedException;
method public java.lang.String readUTF() throws java.io.IOException;
method public java.lang.Object readUnshared() throws java.lang.ClassNotFoundException, java.io.IOException;
method public int readUnsignedByte() throws java.io.IOException;
method public int readUnsignedShort() throws java.io.IOException;
- method public synchronized void registerValidation(java.io.ObjectInputValidation, int) throws java.io.InvalidObjectException, java.io.NotActiveException;
+ method public void registerValidation(java.io.ObjectInputValidation, int) throws java.io.InvalidObjectException, java.io.NotActiveException;
method protected java.lang.Class<?> resolveClass(java.io.ObjectStreamClass) throws java.lang.ClassNotFoundException, java.io.IOException;
method protected java.lang.Object resolveObject(java.lang.Object) throws java.io.IOException;
method protected java.lang.Class<?> resolveProxyClass(java.lang.String[]) throws java.lang.ClassNotFoundException, java.io.IOException;
@@ -45889,16 +46050,16 @@
public static abstract class ObjectInputStream.GetField {
ctor public ObjectInputStream.GetField();
- method public abstract boolean defaulted(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract boolean get(java.lang.String, boolean) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract char get(java.lang.String, char) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract byte get(java.lang.String, byte) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract short get(java.lang.String, short) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract int get(java.lang.String, int) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract long get(java.lang.String, long) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract float get(java.lang.String, float) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract double get(java.lang.String, double) throws java.io.IOException, java.lang.IllegalArgumentException;
- method public abstract java.lang.Object get(java.lang.String, java.lang.Object) throws java.io.IOException, java.lang.IllegalArgumentException;
+ method public abstract boolean defaulted(java.lang.String) throws java.io.IOException;
+ method public abstract boolean get(java.lang.String, boolean) throws java.io.IOException;
+ method public abstract byte get(java.lang.String, byte) throws java.io.IOException;
+ method public abstract char get(java.lang.String, char) throws java.io.IOException;
+ method public abstract short get(java.lang.String, short) throws java.io.IOException;
+ method public abstract int get(java.lang.String, int) throws java.io.IOException;
+ method public abstract long get(java.lang.String, long) throws java.io.IOException;
+ method public abstract float get(java.lang.String, float) throws java.io.IOException;
+ method public abstract double get(java.lang.String, double) throws java.io.IOException;
+ method public abstract java.lang.Object get(java.lang.String, java.lang.Object) throws java.io.IOException;
method public abstract java.io.ObjectStreamClass getObjectStreamClass();
}
@@ -45909,20 +46070,20 @@
public abstract interface ObjectOutput implements java.lang.AutoCloseable java.io.DataOutput {
method public abstract void close() throws java.io.IOException;
method public abstract void flush() throws java.io.IOException;
+ method public abstract void write(int) throws java.io.IOException;
method public abstract void write(byte[]) throws java.io.IOException;
method public abstract void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
method public abstract void writeObject(java.lang.Object) throws java.io.IOException;
}
public class ObjectOutputStream extends java.io.OutputStream implements java.io.ObjectOutput java.io.ObjectStreamConstants {
- ctor protected ObjectOutputStream() throws java.io.IOException;
ctor public ObjectOutputStream(java.io.OutputStream) throws java.io.IOException;
+ ctor protected ObjectOutputStream() throws java.io.IOException, java.lang.SecurityException;
method protected void annotateClass(java.lang.Class<?>) throws java.io.IOException;
method protected void annotateProxyClass(java.lang.Class<?>) throws java.io.IOException;
method public void defaultWriteObject() throws java.io.IOException;
method protected void drain() throws java.io.IOException;
- method protected boolean enableReplaceObject(boolean);
+ method protected boolean enableReplaceObject(boolean) throws java.lang.SecurityException;
method public java.io.ObjectOutputStream.PutField putFields() throws java.io.IOException;
method protected java.lang.Object replaceObject(java.lang.Object) throws java.io.IOException;
method public void reset() throws java.io.IOException;
@@ -45950,8 +46111,8 @@
public static abstract class ObjectOutputStream.PutField {
ctor public ObjectOutputStream.PutField();
method public abstract void put(java.lang.String, boolean);
- method public abstract void put(java.lang.String, char);
method public abstract void put(java.lang.String, byte);
+ method public abstract void put(java.lang.String, char);
method public abstract void put(java.lang.String, short);
method public abstract void put(java.lang.String, int);
method public abstract void put(java.lang.String, long);
@@ -46005,8 +46166,8 @@
}
public abstract class ObjectStreamException extends java.io.IOException {
- ctor protected ObjectStreamException();
ctor protected ObjectStreamException(java.lang.String);
+ ctor protected ObjectStreamException();
}
public class ObjectStreamField implements java.lang.Comparable {
@@ -46032,14 +46193,14 @@
ctor public OutputStream();
method public void close() throws java.io.IOException;
method public void flush() throws java.io.IOException;
+ method public abstract void write(int) throws java.io.IOException;
method public void write(byte[]) throws java.io.IOException;
method public void write(byte[], int, int) throws java.io.IOException;
- method public abstract void write(int) throws java.io.IOException;
}
public class OutputStreamWriter extends java.io.Writer {
- ctor public OutputStreamWriter(java.io.OutputStream);
ctor public OutputStreamWriter(java.io.OutputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public OutputStreamWriter(java.io.OutputStream);
ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.Charset);
ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.CharsetEncoder);
method public void close() throws java.io.IOException;
@@ -46049,10 +46210,10 @@
}
public class PipedInputStream extends java.io.InputStream {
- ctor public PipedInputStream();
ctor public PipedInputStream(java.io.PipedOutputStream) throws java.io.IOException;
- ctor public PipedInputStream(int);
ctor public PipedInputStream(java.io.PipedOutputStream, int) throws java.io.IOException;
+ ctor public PipedInputStream();
+ ctor public PipedInputStream(int);
method public void connect(java.io.PipedOutputStream) throws java.io.IOException;
method public synchronized int read() throws java.io.IOException;
method protected synchronized void receive(int) throws java.io.IOException;
@@ -46063,28 +46224,28 @@
}
public class PipedOutputStream extends java.io.OutputStream {
- ctor public PipedOutputStream();
ctor public PipedOutputStream(java.io.PipedInputStream) throws java.io.IOException;
- method public void connect(java.io.PipedInputStream) throws java.io.IOException;
+ ctor public PipedOutputStream();
+ method public synchronized void connect(java.io.PipedInputStream) throws java.io.IOException;
method public void write(int) throws java.io.IOException;
}
public class PipedReader extends java.io.Reader {
- ctor public PipedReader();
ctor public PipedReader(java.io.PipedWriter) throws java.io.IOException;
- ctor public PipedReader(int);
ctor public PipedReader(java.io.PipedWriter, int) throws java.io.IOException;
- method public synchronized void close() throws java.io.IOException;
+ ctor public PipedReader();
+ ctor public PipedReader(int);
+ method public void close() throws java.io.IOException;
method public void connect(java.io.PipedWriter) throws java.io.IOException;
method public synchronized int read(char[], int, int) throws java.io.IOException;
}
public class PipedWriter extends java.io.Writer {
- ctor public PipedWriter();
ctor public PipedWriter(java.io.PipedReader) throws java.io.IOException;
+ ctor public PipedWriter();
method public void close() throws java.io.IOException;
- method public void connect(java.io.PipedReader) throws java.io.IOException;
- method public void flush() throws java.io.IOException;
+ method public synchronized void connect(java.io.PipedReader) throws java.io.IOException;
+ method public synchronized void flush() throws java.io.IOException;
method public void write(char[], int, int) throws java.io.IOException;
}
@@ -46092,111 +46253,111 @@
ctor public PrintStream(java.io.OutputStream);
ctor public PrintStream(java.io.OutputStream, boolean);
ctor public PrintStream(java.io.OutputStream, boolean, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public PrintStream(java.io.File) throws java.io.FileNotFoundException;
- ctor public PrintStream(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
ctor public PrintStream(java.lang.String) throws java.io.FileNotFoundException;
ctor public PrintStream(java.lang.String, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
- method public java.io.PrintStream append(char);
+ ctor public PrintStream(java.io.File) throws java.io.FileNotFoundException;
+ ctor public PrintStream(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
method public java.io.PrintStream append(java.lang.CharSequence);
method public java.io.PrintStream append(java.lang.CharSequence, int, int);
+ method public java.io.PrintStream append(char);
method public boolean checkError();
method protected void clearError();
method public java.io.PrintStream format(java.lang.String, java.lang.Object...);
method public java.io.PrintStream format(java.util.Locale, java.lang.String, java.lang.Object...);
- method public void print(char[]);
+ method public void print(boolean);
method public void print(char);
- method public void print(double);
- method public void print(float);
method public void print(int);
method public void print(long);
+ method public void print(float);
+ method public void print(double);
+ method public void print(char[]);
+ method public void print(java.lang.String);
method public void print(java.lang.Object);
- method public synchronized void print(java.lang.String);
- method public void print(boolean);
method public java.io.PrintStream printf(java.lang.String, java.lang.Object...);
method public java.io.PrintStream printf(java.util.Locale, java.lang.String, java.lang.Object...);
method public void println();
- method public void println(char[]);
+ method public void println(boolean);
method public void println(char);
- method public void println(double);
- method public void println(float);
method public void println(int);
method public void println(long);
+ method public void println(float);
+ method public void println(double);
+ method public void println(char[]);
+ method public void println(java.lang.String);
method public void println(java.lang.Object);
- method public synchronized void println(java.lang.String);
- method public void println(boolean);
method protected void setError();
}
public class PrintWriter extends java.io.Writer {
- ctor public PrintWriter(java.io.OutputStream);
- ctor public PrintWriter(java.io.OutputStream, boolean);
ctor public PrintWriter(java.io.Writer);
ctor public PrintWriter(java.io.Writer, boolean);
- ctor public PrintWriter(java.io.File) throws java.io.FileNotFoundException;
- ctor public PrintWriter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public PrintWriter(java.io.OutputStream);
+ ctor public PrintWriter(java.io.OutputStream, boolean);
ctor public PrintWriter(java.lang.String) throws java.io.FileNotFoundException;
ctor public PrintWriter(java.lang.String, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public PrintWriter(java.io.File) throws java.io.FileNotFoundException;
+ ctor public PrintWriter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
method public boolean checkError();
method protected void clearError();
method public void close();
method public void flush();
method public java.io.PrintWriter format(java.lang.String, java.lang.Object...);
method public java.io.PrintWriter format(java.util.Locale, java.lang.String, java.lang.Object...);
- method public void print(char[]);
+ method public void print(boolean);
method public void print(char);
- method public void print(double);
- method public void print(float);
method public void print(int);
method public void print(long);
- method public void print(java.lang.Object);
+ method public void print(float);
+ method public void print(double);
+ method public void print(char[]);
method public void print(java.lang.String);
- method public void print(boolean);
+ method public void print(java.lang.Object);
method public java.io.PrintWriter printf(java.lang.String, java.lang.Object...);
method public java.io.PrintWriter printf(java.util.Locale, java.lang.String, java.lang.Object...);
method public void println();
- method public void println(char[]);
+ method public void println(boolean);
method public void println(char);
- method public void println(double);
- method public void println(float);
method public void println(int);
method public void println(long);
- method public void println(java.lang.Object);
+ method public void println(float);
+ method public void println(double);
+ method public void println(char[]);
method public void println(java.lang.String);
- method public void println(boolean);
+ method public void println(java.lang.Object);
method protected void setError();
method public void write(char[], int, int);
field protected java.io.Writer out;
}
public class PushbackInputStream extends java.io.FilterInputStream {
- ctor public PushbackInputStream(java.io.InputStream);
ctor public PushbackInputStream(java.io.InputStream, int);
- method public void unread(byte[]) throws java.io.IOException;
- method public void unread(byte[], int, int) throws java.io.IOException;
+ ctor public PushbackInputStream(java.io.InputStream);
method public void unread(int) throws java.io.IOException;
+ method public void unread(byte[], int, int) throws java.io.IOException;
+ method public void unread(byte[]) throws java.io.IOException;
field protected byte[] buf;
field protected int pos;
}
public class PushbackReader extends java.io.FilterReader {
- ctor public PushbackReader(java.io.Reader);
ctor public PushbackReader(java.io.Reader, int);
- method public void unread(char[]) throws java.io.IOException;
- method public void unread(char[], int, int) throws java.io.IOException;
+ ctor public PushbackReader(java.io.Reader);
method public void unread(int) throws java.io.IOException;
+ method public void unread(char[], int, int) throws java.io.IOException;
+ method public void unread(char[]) throws java.io.IOException;
}
public class RandomAccessFile implements java.io.Closeable java.io.DataInput java.io.DataOutput {
- ctor public RandomAccessFile(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
ctor public RandomAccessFile(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+ ctor public RandomAccessFile(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
method public void close() throws java.io.IOException;
- method public final synchronized java.nio.channels.FileChannel getChannel();
+ method public final java.nio.channels.FileChannel getChannel();
method public final java.io.FileDescriptor getFD() throws java.io.IOException;
method public long getFilePointer() throws java.io.IOException;
method public long length() throws java.io.IOException;
method public int read() throws java.io.IOException;
- method public int read(byte[]) throws java.io.IOException;
method public int read(byte[], int, int) throws java.io.IOException;
+ method public int read(byte[]) throws java.io.IOException;
method public final boolean readBoolean() throws java.io.IOException;
method public final byte readByte() throws java.io.IOException;
method public final char readChar() throws java.io.IOException;
@@ -46214,9 +46375,9 @@
method public void seek(long) throws java.io.IOException;
method public void setLength(long) throws java.io.IOException;
method public int skipBytes(int) throws java.io.IOException;
+ method public void write(int) throws java.io.IOException;
method public void write(byte[]) throws java.io.IOException;
method public void write(byte[], int, int) throws java.io.IOException;
- method public void write(int) throws java.io.IOException;
method public final void writeBoolean(boolean) throws java.io.IOException;
method public final void writeByte(int) throws java.io.IOException;
method public final void writeBytes(java.lang.String) throws java.io.IOException;
@@ -46236,10 +46397,10 @@
method public abstract void close() throws java.io.IOException;
method public void mark(int) throws java.io.IOException;
method public boolean markSupported();
+ method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public int read() throws java.io.IOException;
method public int read(char[]) throws java.io.IOException;
method public abstract int read(char[], int, int) throws java.io.IOException;
- method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public boolean ready() throws java.io.IOException;
method public void reset() throws java.io.IOException;
method public long skip(long) throws java.io.IOException;
@@ -46247,8 +46408,8 @@
}
public class SequenceInputStream extends java.io.InputStream {
- ctor public SequenceInputStream(java.io.InputStream, java.io.InputStream);
ctor public SequenceInputStream(java.util.Enumeration<? extends java.io.InputStream>);
+ ctor public SequenceInputStream(java.io.InputStream, java.io.InputStream);
method public int read() throws java.io.IOException;
}
@@ -46261,8 +46422,8 @@
}
public class StreamCorruptedException extends java.io.ObjectStreamException {
- ctor public StreamCorruptedException();
ctor public StreamCorruptedException(java.lang.String);
+ ctor public StreamCorruptedException();
}
public class StreamTokenizer {
@@ -46337,14 +46498,14 @@
public abstract class Writer implements java.lang.Appendable java.io.Closeable java.io.Flushable {
ctor protected Writer();
ctor protected Writer(java.lang.Object);
- method public java.io.Writer append(char) throws java.io.IOException;
method public java.io.Writer append(java.lang.CharSequence) throws java.io.IOException;
method public java.io.Writer append(java.lang.CharSequence, int, int) throws java.io.IOException;
+ method public java.io.Writer append(char) throws java.io.IOException;
method public abstract void close() throws java.io.IOException;
method public abstract void flush() throws java.io.IOException;
+ method public void write(int) throws java.io.IOException;
method public void write(char[]) throws java.io.IOException;
method public abstract void write(char[], int, int) throws java.io.IOException;
- method public void write(int) throws java.io.IOException;
method public void write(java.lang.String) throws java.io.IOException;
method public void write(java.lang.String, int, int) throws java.io.IOException;
field protected java.lang.Object lock;
@@ -46359,32 +46520,63 @@
ctor public AbstractMethodError(java.lang.String);
}
- abstract class AbstractStringBuilder {
+ abstract class AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence {
+ method public java.lang.AbstractStringBuilder append(java.lang.Object);
+ method public java.lang.AbstractStringBuilder append(java.lang.String);
+ method public java.lang.AbstractStringBuilder append(java.lang.StringBuffer);
+ method public java.lang.AbstractStringBuilder append(java.lang.CharSequence);
+ method public java.lang.AbstractStringBuilder append(java.lang.CharSequence, int, int);
+ method public java.lang.AbstractStringBuilder append(char[]);
+ method public java.lang.AbstractStringBuilder append(char[], int, int);
+ method public java.lang.AbstractStringBuilder append(boolean);
+ method public java.lang.AbstractStringBuilder append(char);
+ method public java.lang.AbstractStringBuilder append(int);
+ method public java.lang.AbstractStringBuilder append(long);
+ method public java.lang.AbstractStringBuilder append(float);
+ method public java.lang.AbstractStringBuilder append(double);
+ method public java.lang.AbstractStringBuilder appendCodePoint(int);
method public int capacity();
method public char charAt(int);
method public int codePointAt(int);
method public int codePointBefore(int);
method public int codePointCount(int, int);
+ method public java.lang.AbstractStringBuilder delete(int, int);
+ method public java.lang.AbstractStringBuilder deleteCharAt(int);
method public void ensureCapacity(int);
method public void getChars(int, int, char[], int);
method public int indexOf(java.lang.String);
method public int indexOf(java.lang.String, int);
+ method public java.lang.AbstractStringBuilder insert(int, char[], int, int);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.Object);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.String);
+ method public java.lang.AbstractStringBuilder insert(int, char[]);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.CharSequence);
+ method public java.lang.AbstractStringBuilder insert(int, java.lang.CharSequence, int, int);
+ method public java.lang.AbstractStringBuilder insert(int, boolean);
+ method public java.lang.AbstractStringBuilder insert(int, char);
+ method public java.lang.AbstractStringBuilder insert(int, int);
+ method public java.lang.AbstractStringBuilder insert(int, long);
+ method public java.lang.AbstractStringBuilder insert(int, float);
+ method public java.lang.AbstractStringBuilder insert(int, double);
method public int lastIndexOf(java.lang.String);
method public int lastIndexOf(java.lang.String, int);
method public int length();
method public int offsetByCodePoints(int, int);
+ method public java.lang.AbstractStringBuilder replace(int, int, java.lang.String);
+ method public java.lang.AbstractStringBuilder reverse();
method public void setCharAt(int, char);
method public void setLength(int);
method public java.lang.CharSequence subSequence(int, int);
method public java.lang.String substring(int);
method public java.lang.String substring(int, int);
+ method public abstract java.lang.String toString();
method public void trimToSize();
}
public abstract interface Appendable {
- method public abstract java.lang.Appendable append(char) throws java.io.IOException;
method public abstract java.lang.Appendable append(java.lang.CharSequence) throws java.io.IOException;
method public abstract java.lang.Appendable append(java.lang.CharSequence, int, int) throws java.io.IOException;
+ method public abstract java.lang.Appendable append(char) throws java.io.IOException;
}
public class ArithmeticException extends java.lang.RuntimeException {
@@ -46405,7 +46597,6 @@
public class AssertionError extends java.lang.Error {
ctor public AssertionError();
- ctor public AssertionError(java.lang.String, java.lang.Throwable);
ctor public AssertionError(java.lang.Object);
ctor public AssertionError(boolean);
ctor public AssertionError(char);
@@ -46413,6 +46604,7 @@
ctor public AssertionError(long);
ctor public AssertionError(float);
ctor public AssertionError(double);
+ ctor public AssertionError(java.lang.String, java.lang.Throwable);
}
public abstract interface AutoCloseable {
@@ -46420,16 +46612,16 @@
}
public final class Boolean implements java.lang.Comparable java.io.Serializable {
- ctor public Boolean(java.lang.String);
ctor public Boolean(boolean);
+ ctor public Boolean(java.lang.String);
method public boolean booleanValue();
method public static int compare(boolean, boolean);
method public int compareTo(java.lang.Boolean);
method public static boolean getBoolean(java.lang.String);
method public static boolean parseBoolean(java.lang.String);
method public static java.lang.String toString(boolean);
- method public static java.lang.Boolean valueOf(java.lang.String);
method public static java.lang.Boolean valueOf(boolean);
+ method public static java.lang.Boolean valueOf(java.lang.String);
field public static final java.lang.Boolean FALSE;
field public static final java.lang.Boolean TRUE;
field public static final java.lang.Class<java.lang.Boolean> TYPE;
@@ -46445,12 +46637,13 @@
method public float floatValue();
method public int intValue();
method public long longValue();
- method public static byte parseByte(java.lang.String) throws java.lang.NumberFormatException;
method public static byte parseByte(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static byte parseByte(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toHexString(byte, boolean);
method public static java.lang.String toString(byte);
- method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
- method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Byte valueOf(byte);
+ method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
field public static final byte MAX_VALUE = 127; // 0x7f
field public static final byte MIN_VALUE = -128; // 0xffffff80
field public static final int SIZE = 8; // 0x8
@@ -46623,7 +46816,7 @@
}
public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
- method public static java.lang.Character.UnicodeBlock forName(java.lang.String);
+ method public static final java.lang.Character.UnicodeBlock forName(java.lang.String);
method public static java.lang.Character.UnicodeBlock of(char);
method public static java.lang.Character.UnicodeBlock of(int);
field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
@@ -46838,6 +47031,109 @@
field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES;
}
+ public static final class Character.UnicodeScript extends java.lang.Enum {
+ method public static final java.lang.Character.UnicodeScript forName(java.lang.String);
+ method public static java.lang.Character.UnicodeScript of(int);
+ method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
+ method public static final java.lang.Character.UnicodeScript[] values();
+ enum_constant public static final java.lang.Character.UnicodeScript ARABIC;
+ enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript AVESTAN;
+ enum_constant public static final java.lang.Character.UnicodeScript BALINESE;
+ enum_constant public static final java.lang.Character.UnicodeScript BAMUM;
+ enum_constant public static final java.lang.Character.UnicodeScript BATAK;
+ enum_constant public static final java.lang.Character.UnicodeScript BENGALI;
+ enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO;
+ enum_constant public static final java.lang.Character.UnicodeScript BRAHMI;
+ enum_constant public static final java.lang.Character.UnicodeScript BRAILLE;
+ enum_constant public static final java.lang.Character.UnicodeScript BUGINESE;
+ enum_constant public static final java.lang.Character.UnicodeScript BUHID;
+ enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL;
+ enum_constant public static final java.lang.Character.UnicodeScript CARIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript CHAM;
+ enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE;
+ enum_constant public static final java.lang.Character.UnicodeScript COMMON;
+ enum_constant public static final java.lang.Character.UnicodeScript COPTIC;
+ enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM;
+ enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT;
+ enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC;
+ enum_constant public static final java.lang.Character.UnicodeScript DESERET;
+ enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI;
+ enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS;
+ enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GOTHIC;
+ enum_constant public static final java.lang.Character.UnicodeScript GREEK;
+ enum_constant public static final java.lang.Character.UnicodeScript GUJARATI;
+ enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI;
+ enum_constant public static final java.lang.Character.UnicodeScript HAN;
+ enum_constant public static final java.lang.Character.UnicodeScript HANGUL;
+ enum_constant public static final java.lang.Character.UnicodeScript HANUNOO;
+ enum_constant public static final java.lang.Character.UnicodeScript HEBREW;
+ enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA;
+ enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC;
+ enum_constant public static final java.lang.Character.UnicodeScript INHERITED;
+ enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI;
+ enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript JAVANESE;
+ enum_constant public static final java.lang.Character.UnicodeScript KAITHI;
+ enum_constant public static final java.lang.Character.UnicodeScript KANNADA;
+ enum_constant public static final java.lang.Character.UnicodeScript KATAKANA;
+ enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI;
+ enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI;
+ enum_constant public static final java.lang.Character.UnicodeScript KHMER;
+ enum_constant public static final java.lang.Character.UnicodeScript LAO;
+ enum_constant public static final java.lang.Character.UnicodeScript LATIN;
+ enum_constant public static final java.lang.Character.UnicodeScript LEPCHA;
+ enum_constant public static final java.lang.Character.UnicodeScript LIMBU;
+ enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B;
+ enum_constant public static final java.lang.Character.UnicodeScript LISU;
+ enum_constant public static final java.lang.Character.UnicodeScript LYCIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript LYDIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM;
+ enum_constant public static final java.lang.Character.UnicodeScript MANDAIC;
+ enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK;
+ enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript MYANMAR;
+ enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE;
+ enum_constant public static final java.lang.Character.UnicodeScript NKO;
+ enum_constant public static final java.lang.Character.UnicodeScript OGHAM;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC;
+ enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI;
+ enum_constant public static final java.lang.Character.UnicodeScript ORIYA;
+ enum_constant public static final java.lang.Character.UnicodeScript OSMANYA;
+ enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA;
+ enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript REJANG;
+ enum_constant public static final java.lang.Character.UnicodeScript RUNIC;
+ enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN;
+ enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA;
+ enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN;
+ enum_constant public static final java.lang.Character.UnicodeScript SINHALA;
+ enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE;
+ enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI;
+ enum_constant public static final java.lang.Character.UnicodeScript SYRIAC;
+ enum_constant public static final java.lang.Character.UnicodeScript TAGALOG;
+ enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_LE;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM;
+ enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET;
+ enum_constant public static final java.lang.Character.UnicodeScript TAMIL;
+ enum_constant public static final java.lang.Character.UnicodeScript TELUGU;
+ enum_constant public static final java.lang.Character.UnicodeScript THAANA;
+ enum_constant public static final java.lang.Character.UnicodeScript THAI;
+ enum_constant public static final java.lang.Character.UnicodeScript TIBETAN;
+ enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH;
+ enum_constant public static final java.lang.Character.UnicodeScript UGARITIC;
+ enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN;
+ enum_constant public static final java.lang.Character.UnicodeScript VAI;
+ enum_constant public static final java.lang.Character.UnicodeScript YI;
+ }
+
public final class Class implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
method public java.lang.Class<? extends U> asSubclass(java.lang.Class<U>);
method public T cast(java.lang.Object);
@@ -46850,28 +47146,28 @@
method public java.lang.ClassLoader getClassLoader();
method public java.lang.Class<?>[] getClasses();
method public java.lang.Class<?> getComponentType();
- method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Constructor<?>[] getConstructors();
+ method public java.lang.reflect.Constructor<T> getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?>[] getDeclaredClasses();
- method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors();
+ method public java.lang.reflect.Constructor<T> getDeclaredConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
method public java.lang.reflect.Field getDeclaredField(java.lang.String) throws java.lang.NoSuchFieldException;
method public java.lang.reflect.Field[] getDeclaredFields();
- method public java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Method[] getDeclaredMethods();
+ method public java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException;
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Class<?> getEnclosingClass();
method public java.lang.reflect.Constructor<?> getEnclosingConstructor();
method public java.lang.reflect.Method getEnclosingMethod();
method public T[] getEnumConstants();
method public java.lang.reflect.Field getField(java.lang.String) throws java.lang.NoSuchFieldException;
- method public java.lang.reflect.Field[] getFields();
+ method public java.lang.reflect.Field[] getFields() throws java.lang.SecurityException;
method public java.lang.reflect.Type[] getGenericInterfaces();
method public java.lang.reflect.Type getGenericSuperclass();
method public java.lang.Class<?>[] getInterfaces();
- method public java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException;
- method public java.lang.reflect.Method[] getMethods();
+ method public java.lang.reflect.Method getMethod(java.lang.String, java.lang.Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+ method public java.lang.reflect.Method[] getMethods() throws java.lang.SecurityException;
method public int getModifiers();
method public java.lang.String getName();
method public java.lang.Package getPackage();
@@ -46913,8 +47209,8 @@
}
public abstract class ClassLoader {
- ctor protected ClassLoader();
ctor protected ClassLoader(java.lang.ClassLoader);
+ ctor protected ClassLoader();
method public void clearAssertionStatus();
method protected final deprecated java.lang.Class<?> defineClass(byte[], int, int) throws java.lang.ClassFormatError;
method protected final java.lang.Class<?> defineClass(java.lang.String, byte[], int, int) throws java.lang.ClassFormatError;
@@ -46939,6 +47235,7 @@
method public static java.util.Enumeration<java.net.URL> getSystemResources(java.lang.String) throws java.io.IOException;
method public java.lang.Class<?> loadClass(java.lang.String) throws java.lang.ClassNotFoundException;
method protected java.lang.Class<?> loadClass(java.lang.String, boolean) throws java.lang.ClassNotFoundException;
+ method protected static boolean registerAsParallelCapable();
method protected final void resolveClass(java.lang.Class<?>);
method public void setClassAssertionStatus(java.lang.String, boolean);
method public void setDefaultAssertionStatus(boolean);
@@ -46986,10 +47283,10 @@
method public double doubleValue();
method public float floatValue();
method public int intValue();
- method public boolean isInfinite();
method public static boolean isInfinite(double);
- method public boolean isNaN();
+ method public boolean isInfinite();
method public static boolean isNaN(double);
+ method public boolean isNaN();
method public static double longBitsToDouble(long);
method public long longValue();
method public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException;
@@ -47033,6 +47330,7 @@
ctor public Error(java.lang.String);
ctor public Error(java.lang.String, java.lang.Throwable);
ctor public Error(java.lang.Throwable);
+ ctor protected Error(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public class Exception extends java.lang.Throwable {
@@ -47040,12 +47338,13 @@
ctor public Exception(java.lang.String);
ctor public Exception(java.lang.String, java.lang.Throwable);
ctor public Exception(java.lang.Throwable);
+ ctor protected Exception(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public class ExceptionInInitializerError extends java.lang.LinkageError {
ctor public ExceptionInInitializerError();
- ctor public ExceptionInInitializerError(java.lang.String);
ctor public ExceptionInInitializerError(java.lang.Throwable);
+ ctor public ExceptionInInitializerError(java.lang.String);
method public java.lang.Throwable getException();
}
@@ -47061,10 +47360,10 @@
method public float floatValue();
method public static float intBitsToFloat(int);
method public int intValue();
- method public boolean isInfinite();
method public static boolean isInfinite(float);
- method public boolean isNaN();
+ method public boolean isInfinite();
method public static boolean isNaN(float);
+ method public boolean isNaN();
method public long longValue();
method public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.String toHexString(float);
@@ -47160,8 +47459,8 @@
method public static int lowestOneBit(int);
method public static int numberOfLeadingZeros(int);
method public static int numberOfTrailingZeros(int);
- method public static int parseInt(java.lang.String) throws java.lang.NumberFormatException;
method public static int parseInt(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static int parseInt(java.lang.String) throws java.lang.NumberFormatException;
method public static int reverse(int);
method public static int reverseBytes(int);
method public static int rotateLeft(int, int);
@@ -47170,10 +47469,10 @@
method public static java.lang.String toBinaryString(int);
method public static java.lang.String toHexString(int);
method public static java.lang.String toOctalString(int);
- method public static java.lang.String toString(int);
method public static java.lang.String toString(int, int);
- method public static java.lang.Integer valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toString(int);
method public static java.lang.Integer valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Integer valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Integer valueOf(int);
field public static final int MAX_VALUE = 2147483647; // 0x7fffffff
field public static final int MIN_VALUE = -2147483648; // 0x80000000
@@ -47219,8 +47518,8 @@
method public static long lowestOneBit(long);
method public static int numberOfLeadingZeros(long);
method public static int numberOfTrailingZeros(long);
- method public static long parseLong(java.lang.String) throws java.lang.NumberFormatException;
method public static long parseLong(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static long parseLong(java.lang.String) throws java.lang.NumberFormatException;
method public static long reverse(long);
method public static long reverseBytes(long);
method public static long rotateLeft(long, int);
@@ -47229,10 +47528,10 @@
method public static java.lang.String toBinaryString(long);
method public static java.lang.String toHexString(long);
method public static java.lang.String toOctalString(long);
- method public static java.lang.String toString(long);
method public static java.lang.String toString(long, int);
- method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ method public static java.lang.String toString(long);
method public static java.lang.Long valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(long);
field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
@@ -47242,10 +47541,10 @@
public final class Math {
method public static double IEEEremainder(double, double);
- method public static double abs(double);
- method public static float abs(float);
method public static int abs(int);
method public static long abs(long);
+ method public static float abs(float);
+ method public static double abs(double);
method public static double acos(double);
method public static double asin(double);
method public static double atan(double);
@@ -47265,14 +47564,14 @@
method public static double log(double);
method public static double log10(double);
method public static double log1p(double);
- method public static double max(double, double);
- method public static float max(float, float);
method public static int max(int, int);
method public static long max(long, long);
- method public static double min(double, double);
- method public static float min(float, float);
+ method public static float max(float, float);
+ method public static double max(double, double);
method public static int min(int, int);
method public static long min(long, long);
+ method public static float min(float, float);
+ method public static double min(double, double);
method public static double nextAfter(double, double);
method public static float nextAfter(float, double);
method public static double nextUp(double);
@@ -47280,8 +47579,8 @@
method public static double pow(double, double);
method public static double random();
method public static double rint(double);
- method public static long round(double);
method public static int round(float);
+ method public static long round(double);
method public static double scalb(double, int);
method public static float scalb(float, int);
method public static double signum(double);
@@ -47359,9 +47658,9 @@
method public final void notify();
method public final void notifyAll();
method public java.lang.String toString();
- method public final void wait() throws java.lang.InterruptedException;
method public final void wait(long) throws java.lang.InterruptedException;
method public final void wait(long, int) throws java.lang.InterruptedException;
+ method public final void wait() throws java.lang.InterruptedException;
}
public class OutOfMemoryError extends java.lang.VirtualMachineError {
@@ -47402,19 +47701,49 @@
}
public final class ProcessBuilder {
- ctor public ProcessBuilder(java.lang.String...);
ctor public ProcessBuilder(java.util.List<java.lang.String>);
- method public java.util.List<java.lang.String> command();
- method public java.lang.ProcessBuilder command(java.lang.String...);
+ ctor public ProcessBuilder(java.lang.String...);
method public java.lang.ProcessBuilder command(java.util.List<java.lang.String>);
+ method public java.lang.ProcessBuilder command(java.lang.String...);
+ method public java.util.List<java.lang.String> command();
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
+ method public java.lang.ProcessBuilder inheritIO();
+ method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectError(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
+ method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectInput(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectInput();
+ method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
+ method public java.lang.ProcessBuilder redirectOutput(java.io.File);
+ method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
+ public static abstract class ProcessBuilder.Redirect {
+ method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
+ method public java.io.File file();
+ method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
+ method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
+ method public abstract java.lang.ProcessBuilder.Redirect.Type type();
+ field public static final java.lang.ProcessBuilder.Redirect INHERIT;
+ field public static final java.lang.ProcessBuilder.Redirect PIPE;
+ }
+
+ public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
+ method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
+ method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
+ enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
+ }
+
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
@@ -47422,8 +47751,8 @@
public class ReflectiveOperationException extends java.lang.Exception {
ctor public ReflectiveOperationException();
ctor public ReflectiveOperationException(java.lang.String);
- ctor public ReflectiveOperationException(java.lang.Throwable);
ctor public ReflectiveOperationException(java.lang.String, java.lang.Throwable);
+ ctor public ReflectiveOperationException(java.lang.Throwable);
}
public abstract interface Runnable {
@@ -47433,12 +47762,12 @@
public class Runtime {
method public void addShutdownHook(java.lang.Thread);
method public int availableProcessors();
- method public java.lang.Process exec(java.lang.String[]) throws java.io.IOException;
- method public java.lang.Process exec(java.lang.String[], java.lang.String[]) throws java.io.IOException;
- method public java.lang.Process exec(java.lang.String[], java.lang.String[], java.io.File) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String, java.lang.String[]) throws java.io.IOException;
method public java.lang.Process exec(java.lang.String, java.lang.String[], java.io.File) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[]) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[], java.lang.String[]) throws java.io.IOException;
+ method public java.lang.Process exec(java.lang.String[], java.lang.String[], java.io.File) throws java.io.IOException;
method public void exit(int);
method public long freeMemory();
method public void gc();
@@ -47462,6 +47791,7 @@
ctor public RuntimeException(java.lang.String);
ctor public RuntimeException(java.lang.String, java.lang.Throwable);
ctor public RuntimeException(java.lang.Throwable);
+ ctor protected RuntimeException(java.lang.String, java.lang.Throwable, boolean, boolean);
}
public final class RuntimePermission extends java.security.BasicPermission {
@@ -47526,8 +47856,8 @@
}
public final class Short extends java.lang.Number implements java.lang.Comparable {
- ctor public Short(java.lang.String) throws java.lang.NumberFormatException;
ctor public Short(short);
+ ctor public Short(java.lang.String) throws java.lang.NumberFormatException;
method public static int compare(short, short);
method public int compareTo(java.lang.Short);
method public static java.lang.Short decode(java.lang.String) throws java.lang.NumberFormatException;
@@ -47535,12 +47865,12 @@
method public float floatValue();
method public int intValue();
method public long longValue();
- method public static short parseShort(java.lang.String) throws java.lang.NumberFormatException;
method public static short parseShort(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static short parseShort(java.lang.String) throws java.lang.NumberFormatException;
method public static short reverseBytes(short);
method public static java.lang.String toString(short);
- method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
+ method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(short);
field public static final short MAX_VALUE = 32767; // 0x7fff
field public static final short MIN_VALUE = -32768; // 0xffff8000
@@ -47564,10 +47894,10 @@
public final class StrictMath {
method public static double IEEEremainder(double, double);
- method public static double abs(double);
- method public static float abs(float);
method public static int abs(int);
method public static long abs(long);
+ method public static float abs(float);
+ method public static double abs(double);
method public static double acos(double);
method public static double asin(double);
method public static double atan(double);
@@ -47587,14 +47917,14 @@
method public static double log(double);
method public static double log10(double);
method public static double log1p(double);
- method public static double max(double, double);
- method public static float max(float, float);
method public static int max(int, int);
method public static long max(long, long);
- method public static double min(double, double);
- method public static float min(float, float);
+ method public static float max(float, float);
+ method public static double max(double, double);
method public static int min(int, int);
method public static long min(long, long);
+ method public static float min(float, float);
+ method public static double min(double, double);
method public static double nextAfter(double, double);
method public static float nextAfter(float, double);
method public static double nextUp(double);
@@ -47602,8 +47932,8 @@
method public static double pow(double, double);
method public static double random();
method public static double rint(double);
- method public static long round(double);
method public static int round(float);
+ method public static long round(double);
method public static double scalb(double, int);
method public static float scalb(float, int);
method public static double signum(double);
@@ -47623,19 +47953,19 @@
public final class String implements java.lang.CharSequence java.lang.Comparable java.io.Serializable {
ctor public String();
- ctor public String(byte[]);
- ctor public deprecated String(byte[], int);
- ctor public String(byte[], int, int);
- ctor public deprecated String(byte[], int, int, int);
- ctor public String(byte[], int, int, java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public String(byte[], java.lang.String) throws java.io.UnsupportedEncodingException;
- ctor public String(byte[], int, int, java.nio.charset.Charset);
- ctor public String(byte[], java.nio.charset.Charset);
+ ctor public String(java.lang.String);
ctor public String(char[]);
ctor public String(char[], int, int);
- ctor public String(java.lang.String);
- ctor public String(java.lang.StringBuffer);
ctor public String(int[], int, int);
+ ctor public deprecated String(byte[], int, int, int);
+ ctor public deprecated String(byte[], int);
+ ctor public String(byte[], int, int, java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public String(byte[], int, int, java.nio.charset.Charset);
+ ctor public String(byte[], java.lang.String) throws java.io.UnsupportedEncodingException;
+ ctor public String(byte[], java.nio.charset.Charset);
+ ctor public String(byte[], int, int);
+ ctor public String(byte[]);
+ ctor public String(java.lang.StringBuffer);
ctor public String(java.lang.StringBuilder);
method public char charAt(int);
method public int codePointAt(int);
@@ -47647,16 +47977,16 @@
method public boolean contains(java.lang.CharSequence);
method public boolean contentEquals(java.lang.StringBuffer);
method public boolean contentEquals(java.lang.CharSequence);
- method public static java.lang.String copyValueOf(char[]);
method public static java.lang.String copyValueOf(char[], int, int);
+ method public static java.lang.String copyValueOf(char[]);
method public boolean endsWith(java.lang.String);
method public boolean equalsIgnoreCase(java.lang.String);
method public static java.lang.String format(java.lang.String, java.lang.Object...);
method public static java.lang.String format(java.util.Locale, java.lang.String, java.lang.Object...);
method public deprecated void getBytes(int, int, byte[], int);
- method public byte[] getBytes();
method public byte[] getBytes(java.lang.String) throws java.io.UnsupportedEncodingException;
method public byte[] getBytes(java.nio.charset.Charset);
+ method public byte[] getBytes();
method public void getChars(int, int, char[], int);
method public int indexOf(int);
method public int indexOf(int, int);
@@ -47677,109 +48007,51 @@
method public java.lang.String replace(java.lang.CharSequence, java.lang.CharSequence);
method public java.lang.String replaceAll(java.lang.String, java.lang.String);
method public java.lang.String replaceFirst(java.lang.String, java.lang.String);
- method public java.lang.String[] split(java.lang.String);
method public java.lang.String[] split(java.lang.String, int);
- method public boolean startsWith(java.lang.String);
+ method public java.lang.String[] split(java.lang.String);
method public boolean startsWith(java.lang.String, int);
+ method public boolean startsWith(java.lang.String);
method public java.lang.CharSequence subSequence(int, int);
method public java.lang.String substring(int);
method public java.lang.String substring(int, int);
method public char[] toCharArray();
- method public java.lang.String toLowerCase();
method public java.lang.String toLowerCase(java.util.Locale);
- method public java.lang.String toUpperCase();
+ method public java.lang.String toLowerCase();
method public java.lang.String toUpperCase(java.util.Locale);
+ method public java.lang.String toUpperCase();
method public java.lang.String trim();
+ method public static java.lang.String valueOf(java.lang.Object);
method public static java.lang.String valueOf(char[]);
method public static java.lang.String valueOf(char[], int, int);
+ method public static java.lang.String valueOf(boolean);
method public static java.lang.String valueOf(char);
- method public static java.lang.String valueOf(double);
- method public static java.lang.String valueOf(float);
method public static java.lang.String valueOf(int);
method public static java.lang.String valueOf(long);
- method public static java.lang.String valueOf(java.lang.Object);
- method public static java.lang.String valueOf(boolean);
+ method public static java.lang.String valueOf(float);
+ method public static java.lang.String valueOf(double);
field public static final java.util.Comparator<java.lang.String> CASE_INSENSITIVE_ORDER;
}
- public final class StringBuffer extends java.lang.AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+ public final class StringBuffer extends java.lang.AbstractStringBuilder implements java.lang.CharSequence java.io.Serializable {
ctor public StringBuffer();
ctor public StringBuffer(int);
ctor public StringBuffer(java.lang.String);
ctor public StringBuffer(java.lang.CharSequence);
- method public java.lang.StringBuffer append(boolean);
- method public synchronized java.lang.StringBuffer append(char);
- method public java.lang.StringBuffer append(double);
- method public java.lang.StringBuffer append(float);
- method public java.lang.StringBuffer append(int);
- method public java.lang.StringBuffer append(long);
- method public synchronized java.lang.StringBuffer append(java.lang.Object);
- method public synchronized java.lang.StringBuffer append(java.lang.String);
- method public synchronized java.lang.StringBuffer append(java.lang.StringBuffer);
- method public synchronized java.lang.StringBuffer append(char[]);
- method public synchronized java.lang.StringBuffer append(char[], int, int);
- method public synchronized java.lang.StringBuffer append(java.lang.CharSequence);
- method public synchronized java.lang.StringBuffer append(java.lang.CharSequence, int, int);
- method public java.lang.StringBuffer appendCodePoint(int);
- method public synchronized java.lang.StringBuffer delete(int, int);
- method public synchronized java.lang.StringBuffer deleteCharAt(int);
- method public synchronized java.lang.StringBuffer insert(int, char);
- method public java.lang.StringBuffer insert(int, boolean);
- method public java.lang.StringBuffer insert(int, int);
- method public java.lang.StringBuffer insert(int, long);
- method public java.lang.StringBuffer insert(int, double);
- method public java.lang.StringBuffer insert(int, float);
- method public java.lang.StringBuffer insert(int, java.lang.Object);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.String);
- method public synchronized java.lang.StringBuffer insert(int, char[]);
- method public synchronized java.lang.StringBuffer insert(int, char[], int, int);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.CharSequence);
- method public synchronized java.lang.StringBuffer insert(int, java.lang.CharSequence, int, int);
- method public synchronized java.lang.StringBuffer replace(int, int, java.lang.String);
- method public synchronized java.lang.StringBuffer reverse();
+ method public synchronized java.lang.String toString();
}
- public final class StringBuilder extends java.lang.AbstractStringBuilder implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+ public final class StringBuilder extends java.lang.AbstractStringBuilder implements java.lang.CharSequence java.io.Serializable {
ctor public StringBuilder();
ctor public StringBuilder(int);
- ctor public StringBuilder(java.lang.CharSequence);
ctor public StringBuilder(java.lang.String);
- method public java.lang.StringBuilder append(boolean);
- method public java.lang.StringBuilder append(char);
- method public java.lang.StringBuilder append(int);
- method public java.lang.StringBuilder append(long);
- method public java.lang.StringBuilder append(float);
- method public java.lang.StringBuilder append(double);
- method public java.lang.StringBuilder append(java.lang.Object);
- method public java.lang.StringBuilder append(java.lang.String);
- method public java.lang.StringBuilder append(java.lang.StringBuffer);
- method public java.lang.StringBuilder append(char[]);
- method public java.lang.StringBuilder append(char[], int, int);
- method public java.lang.StringBuilder append(java.lang.CharSequence);
- method public java.lang.StringBuilder append(java.lang.CharSequence, int, int);
- method public java.lang.StringBuilder appendCodePoint(int);
- method public java.lang.StringBuilder delete(int, int);
- method public java.lang.StringBuilder deleteCharAt(int);
- method public java.lang.StringBuilder insert(int, boolean);
- method public java.lang.StringBuilder insert(int, char);
- method public java.lang.StringBuilder insert(int, int);
- method public java.lang.StringBuilder insert(int, long);
- method public java.lang.StringBuilder insert(int, float);
- method public java.lang.StringBuilder insert(int, double);
- method public java.lang.StringBuilder insert(int, java.lang.Object);
- method public java.lang.StringBuilder insert(int, java.lang.String);
- method public java.lang.StringBuilder insert(int, char[]);
- method public java.lang.StringBuilder insert(int, char[], int, int);
- method public java.lang.StringBuilder insert(int, java.lang.CharSequence);
- method public java.lang.StringBuilder insert(int, java.lang.CharSequence, int, int);
- method public java.lang.StringBuilder replace(int, int, java.lang.String);
- method public java.lang.StringBuilder reverse();
+ ctor public StringBuilder(java.lang.CharSequence);
+ method public java.lang.String toString();
}
public class StringIndexOutOfBoundsException extends java.lang.IndexOutOfBoundsException {
ctor public StringIndexOutOfBoundsException();
- ctor public StringIndexOutOfBoundsException(int);
ctor public StringIndexOutOfBoundsException(java.lang.String);
+ ctor public StringIndexOutOfBoundsException(int);
}
public abstract class SuppressWarnings implements java.lang.annotation.Annotation {
@@ -47821,11 +48093,11 @@
public class Thread implements java.lang.Runnable {
ctor public Thread();
ctor public Thread(java.lang.Runnable);
- ctor public Thread(java.lang.Runnable, java.lang.String);
- ctor public Thread(java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable);
- ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String);
+ ctor public Thread(java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.String);
+ ctor public Thread(java.lang.Runnable, java.lang.String);
+ ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String);
ctor public Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long);
method public static int activeCount();
method public final void checkAccess();
@@ -47850,9 +48122,9 @@
method public final boolean isAlive();
method public final boolean isDaemon();
method public boolean isInterrupted();
- method public final void join() throws java.lang.InterruptedException;
method public final void join(long) throws java.lang.InterruptedException;
method public final void join(long, int) throws java.lang.InterruptedException;
+ method public final void join() throws java.lang.InterruptedException;
method public final deprecated void resume();
method public void run();
method public void setContextClassLoader(java.lang.ClassLoader);
@@ -47865,7 +48137,7 @@
method public static void sleep(long, int) throws java.lang.InterruptedException;
method public synchronized void start();
method public final deprecated void stop();
- method public final deprecated synchronized void stop(java.lang.Throwable);
+ method public final deprecated void stop(java.lang.Throwable);
method public final deprecated void suspend();
method public static void yield();
field public static final int MAX_PRIORITY = 10; // 0xa
@@ -47934,14 +48206,14 @@
ctor public Throwable(java.lang.String, java.lang.Throwable);
ctor public Throwable(java.lang.Throwable);
ctor protected Throwable(java.lang.String, java.lang.Throwable, boolean, boolean);
- method public final void addSuppressed(java.lang.Throwable);
- method public java.lang.Throwable fillInStackTrace();
- method public java.lang.Throwable getCause();
+ method public final synchronized void addSuppressed(java.lang.Throwable);
+ method public synchronized java.lang.Throwable fillInStackTrace();
+ method public synchronized java.lang.Throwable getCause();
method public java.lang.String getLocalizedMessage();
method public java.lang.String getMessage();
method public java.lang.StackTraceElement[] getStackTrace();
- method public final java.lang.Throwable[] getSuppressed();
- method public java.lang.Throwable initCause(java.lang.Throwable);
+ method public final synchronized java.lang.Throwable[] getSuppressed();
+ method public synchronized java.lang.Throwable initCause(java.lang.Throwable);
method public void printStackTrace();
method public void printStackTrace(java.io.PrintStream);
method public void printStackTrace(java.io.PrintWriter);
@@ -48062,15 +48334,16 @@
public abstract class Reference {
method public void clear();
method public boolean enqueue();
+ method public final synchronized boolean enqueueInternal();
method public T get();
method public boolean isEnqueued();
}
public class ReferenceQueue {
ctor public ReferenceQueue();
- method public synchronized java.lang.ref.Reference<? extends T> poll();
+ method public java.lang.ref.Reference<? extends T> poll();
+ method public java.lang.ref.Reference<? extends T> remove(long) throws java.lang.IllegalArgumentException, java.lang.InterruptedException;
method public java.lang.ref.Reference<? extends T> remove() throws java.lang.InterruptedException;
- method public synchronized java.lang.ref.Reference<? extends T> remove(long) throws java.lang.InterruptedException;
}
public class SoftReference extends java.lang.ref.Reference {
@@ -48094,8 +48367,8 @@
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public boolean isAccessible();
method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
- method public void setAccessible(boolean);
- method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean);
+ method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
+ method public void setAccessible(boolean) throws java.lang.SecurityException;
}
public abstract interface AnnotatedElement {
@@ -48116,8 +48389,9 @@
method public static int getLength(java.lang.Object);
method public static long getLong(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static short getShort(java.lang.Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
- method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
+ method public static java.lang.Object newArray(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
method public static java.lang.Object newInstance(java.lang.Class<?>, int) throws java.lang.NegativeArraySizeException;
+ method public static java.lang.Object newInstance(java.lang.Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
method public static void set(java.lang.Object, int, java.lang.Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
method public static void setBoolean(java.lang.Object, int, boolean);
method public static void setByte(java.lang.Object, int, byte) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
@@ -48132,7 +48406,6 @@
public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
- method public java.lang.annotation.Annotation[] getAnnotations();
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<T> getDeclaringClass();
method public java.lang.Class<?>[] getExceptionTypes();
@@ -48219,7 +48492,6 @@
public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
- method public java.lang.annotation.Annotation[] getAnnotations();
method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Object getDefaultValue();
@@ -48500,10 +48772,10 @@
method protected final java.net.InetAddress getRequestingSite();
method protected java.net.URL getRequestingURL();
method protected java.net.Authenticator.RequestorType getRequestorType();
- method public static synchronized java.net.PasswordAuthentication requestPasswordAuthentication(java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
- method public static synchronized java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
+ method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
+ method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String);
method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.lang.String, java.net.InetAddress, int, java.lang.String, java.lang.String, java.lang.String, java.net.URL, java.net.Authenticator.RequestorType);
- method public static void setDefault(java.net.Authenticator);
+ method public static synchronized void setDefault(java.net.Authenticator);
}
public static final class Authenticator.RequestorType extends java.lang.Enum {
@@ -48514,8 +48786,9 @@
}
public class BindException extends java.net.SocketException {
- ctor public BindException();
ctor public BindException(java.lang.String);
+ ctor public BindException();
+ ctor public BindException(java.lang.String, java.lang.Throwable);
}
public abstract class CacheRequest {
@@ -48531,8 +48804,9 @@
}
public class ConnectException extends java.net.SocketException {
- ctor public ConnectException();
ctor public ConnectException(java.lang.String);
+ ctor public ConnectException();
+ ctor public ConnectException(java.lang.String, java.lang.Throwable);
}
public abstract class ContentHandler {
@@ -48548,9 +48822,9 @@
public abstract class CookieHandler {
ctor public CookieHandler();
method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> get(java.net.URI, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static java.net.CookieHandler getDefault();
+ method public static synchronized java.net.CookieHandler getDefault();
method public abstract void put(java.net.URI, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static void setDefault(java.net.CookieHandler);
+ method public static synchronized void setDefault(java.net.CookieHandler);
}
public class CookieManager extends java.net.CookieHandler {
@@ -48579,12 +48853,12 @@
}
public final class DatagramPacket {
- ctor public DatagramPacket(byte[], int);
ctor public DatagramPacket(byte[], int, int);
+ ctor public DatagramPacket(byte[], int);
ctor public DatagramPacket(byte[], int, int, java.net.InetAddress, int);
+ ctor public DatagramPacket(byte[], int, int, java.net.SocketAddress) throws java.net.SocketException;
ctor public DatagramPacket(byte[], int, java.net.InetAddress, int);
ctor public DatagramPacket(byte[], int, java.net.SocketAddress) throws java.net.SocketException;
- ctor public DatagramPacket(byte[], int, int, java.net.SocketAddress) throws java.net.SocketException;
method public synchronized java.net.InetAddress getAddress();
method public synchronized byte[] getData();
method public synchronized int getLength();
@@ -48601,17 +48875,18 @@
public class DatagramSocket implements java.io.Closeable {
ctor public DatagramSocket() throws java.net.SocketException;
- ctor public DatagramSocket(int) throws java.net.SocketException;
- ctor public DatagramSocket(int, java.net.InetAddress) throws java.net.SocketException;
ctor protected DatagramSocket(java.net.DatagramSocketImpl);
ctor public DatagramSocket(java.net.SocketAddress) throws java.net.SocketException;
- method public void bind(java.net.SocketAddress) throws java.net.SocketException;
+ ctor public DatagramSocket(int) throws java.net.SocketException;
+ ctor public DatagramSocket(int, java.net.InetAddress) throws java.net.SocketException;
+ method public synchronized void bind(java.net.SocketAddress) throws java.net.SocketException;
method public void close();
- method public void connect(java.net.SocketAddress) throws java.net.SocketException;
method public void connect(java.net.InetAddress, int);
+ method public void connect(java.net.SocketAddress) throws java.net.SocketException;
method public void disconnect();
- method public boolean getBroadcast() throws java.net.SocketException;
+ method public synchronized boolean getBroadcast() throws java.net.SocketException;
method public java.nio.channels.DatagramChannel getChannel();
+ method public final java.io.FileDescriptor getFileDescriptor$();
method public java.net.InetAddress getInetAddress();
method public java.net.InetAddress getLocalAddress();
method public int getLocalPort();
@@ -48619,22 +48894,22 @@
method public int getPort();
method public synchronized int getReceiveBufferSize() throws java.net.SocketException;
method public java.net.SocketAddress getRemoteSocketAddress();
- method public boolean getReuseAddress() throws java.net.SocketException;
+ method public synchronized boolean getReuseAddress() throws java.net.SocketException;
method public synchronized int getSendBufferSize() throws java.net.SocketException;
method public synchronized int getSoTimeout() throws java.net.SocketException;
- method public int getTrafficClass() throws java.net.SocketException;
+ method public synchronized int getTrafficClass() throws java.net.SocketException;
method public boolean isBound();
method public boolean isClosed();
method public boolean isConnected();
method public synchronized void receive(java.net.DatagramPacket) throws java.io.IOException;
method public void send(java.net.DatagramPacket) throws java.io.IOException;
- method public void setBroadcast(boolean) throws java.net.SocketException;
+ method public synchronized void setBroadcast(boolean) throws java.net.SocketException;
method public static synchronized void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory) throws java.io.IOException;
method public synchronized void setReceiveBufferSize(int) throws java.net.SocketException;
- method public void setReuseAddress(boolean) throws java.net.SocketException;
+ method public synchronized void setReuseAddress(boolean) throws java.net.SocketException;
method public synchronized void setSendBufferSize(int) throws java.net.SocketException;
method public synchronized void setSoTimeout(int) throws java.net.SocketException;
- method public void setTrafficClass(int) throws java.net.SocketException;
+ method public synchronized void setTrafficClass(int) throws java.net.SocketException;
}
public abstract class DatagramSocketImpl implements java.net.SocketOptions {
@@ -48686,11 +48961,14 @@
method public java.lang.String getValue();
method public int getVersion();
method public boolean hasExpired();
+ method public boolean isHttpOnly();
method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
+ method public static java.util.List<java.net.HttpCookie> parse(java.lang.String, boolean);
method public void setComment(java.lang.String);
method public void setCommentURL(java.lang.String);
method public void setDiscard(boolean);
method public void setDomain(java.lang.String);
+ method public void setHttpOnly(boolean);
method public void setMaxAge(long);
method public void setPath(java.lang.String);
method public void setPortlist(java.lang.String);
@@ -48717,8 +48995,8 @@
method public int getResponseCode() throws java.io.IOException;
method public java.lang.String getResponseMessage() throws java.io.IOException;
method public void setChunkedStreamingMode(int);
- method public void setFixedLengthStreamingMode(long);
method public void setFixedLengthStreamingMode(int);
+ method public void setFixedLengthStreamingMode(long);
method public static void setFollowRedirects(boolean);
method public void setInstanceFollowRedirects(boolean);
method public void setRequestMethod(java.lang.String) throws java.net.ProtocolException;
@@ -48778,21 +49056,27 @@
}
public final class Inet4Address extends java.net.InetAddress {
+ field public static final java.net.InetAddress ALL;
+ field public static final java.net.InetAddress ANY;
+ field public static final java.net.InetAddress LOOPBACK;
}
public final class Inet6Address extends java.net.InetAddress {
- method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], int) throws java.net.UnknownHostException;
method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], java.net.NetworkInterface) throws java.net.UnknownHostException;
+ method public static java.net.Inet6Address getByAddress(java.lang.String, byte[], int) throws java.net.UnknownHostException;
method public int getScopeId();
method public java.net.NetworkInterface getScopedInterface();
method public boolean isIPv4CompatibleAddress();
+ field public static final java.net.InetAddress ANY;
+ field public static final java.net.InetAddress LOOPBACK;
}
public class InetAddress implements java.io.Serializable {
method public byte[] getAddress();
+ method public byte[] getAddressInternal();
method public static java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
- method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByAddress(java.lang.String, byte[]) throws java.net.UnknownHostException;
+ method public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
method public static java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
method public java.lang.String getCanonicalHostName();
method public java.lang.String getHostAddress();
@@ -48899,8 +49183,8 @@
}
public class NoRouteToHostException extends java.net.SocketException {
- ctor public NoRouteToHostException();
ctor public NoRouteToHostException(java.lang.String);
+ ctor public NoRouteToHostException();
}
public final class PasswordAuthentication {
@@ -48910,13 +49194,19 @@
}
public class PortUnreachableException extends java.net.SocketException {
- ctor public PortUnreachableException();
ctor public PortUnreachableException(java.lang.String);
+ ctor public PortUnreachableException();
+ ctor public PortUnreachableException(java.lang.String, java.lang.Throwable);
}
public class ProtocolException extends java.io.IOException {
- ctor public ProtocolException();
ctor public ProtocolException(java.lang.String);
+ ctor public ProtocolException();
+ ctor public ProtocolException(java.lang.String, java.lang.Throwable);
+ }
+
+ public abstract interface ProtocolFamily {
+ method public abstract java.lang.String name();
}
public class Proxy {
@@ -48947,9 +49237,9 @@
public abstract class ResponseCache {
ctor public ResponseCache();
method public abstract java.net.CacheResponse get(java.net.URI, java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) throws java.io.IOException;
- method public static java.net.ResponseCache getDefault();
+ method public static synchronized java.net.ResponseCache getDefault();
method public abstract java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException;
- method public static void setDefault(java.net.ResponseCache);
+ method public static synchronized void setDefault(java.net.ResponseCache);
}
public abstract class SecureCacheResponse extends java.net.CacheResponse {
@@ -48974,14 +49264,14 @@
method public java.net.InetAddress getInetAddress();
method public int getLocalPort();
method public java.net.SocketAddress getLocalSocketAddress();
- method public int getReceiveBufferSize() throws java.net.SocketException;
+ method public synchronized int getReceiveBufferSize() throws java.net.SocketException;
method public boolean getReuseAddress() throws java.net.SocketException;
method public synchronized int getSoTimeout() throws java.io.IOException;
method protected final void implAccept(java.net.Socket) throws java.io.IOException;
method public boolean isBound();
method public boolean isClosed();
method public void setPerformancePreferences(int, int, int);
- method public void setReceiveBufferSize(int) throws java.net.SocketException;
+ method public synchronized void setReceiveBufferSize(int) throws java.net.SocketException;
method public void setReuseAddress(boolean) throws java.net.SocketException;
method public synchronized void setSoTimeout(int) throws java.net.SocketException;
method public static synchronized void setSocketFactory(java.net.SocketImplFactory) throws java.io.IOException;
@@ -48990,13 +49280,13 @@
public class Socket implements java.io.Closeable {
ctor public Socket();
ctor public Socket(java.net.Proxy);
- ctor public Socket(java.lang.String, int) throws java.io.IOException, java.net.UnknownHostException;
- ctor public Socket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException;
- ctor public deprecated Socket(java.lang.String, int, boolean) throws java.io.IOException;
- ctor public Socket(java.net.InetAddress, int) throws java.io.IOException;
- ctor public Socket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
- ctor public deprecated Socket(java.net.InetAddress, int, boolean) throws java.io.IOException;
ctor protected Socket(java.net.SocketImpl) throws java.net.SocketException;
+ ctor public Socket(java.lang.String, int) throws java.io.IOException, java.net.UnknownHostException;
+ ctor public Socket(java.net.InetAddress, int) throws java.io.IOException;
+ ctor public Socket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException;
+ ctor public Socket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+ ctor public deprecated Socket(java.lang.String, int, boolean) throws java.io.IOException;
+ ctor public deprecated Socket(java.net.InetAddress, int, boolean) throws java.io.IOException;
method public void bind(java.net.SocketAddress) throws java.io.IOException;
method public synchronized void close() throws java.io.IOException;
method public void connect(java.net.SocketAddress) throws java.io.IOException;
@@ -49045,8 +49335,10 @@
}
public class SocketException extends java.io.IOException {
- ctor public SocketException();
ctor public SocketException(java.lang.String);
+ ctor public SocketException();
+ ctor public SocketException(java.lang.Throwable);
+ ctor public SocketException(java.lang.String, java.lang.Throwable);
}
public abstract class SocketImpl implements java.net.SocketOptions {
@@ -49059,7 +49351,7 @@
method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
method protected abstract void create(boolean) throws java.io.IOException;
- method protected java.io.FileDescriptor getFileDescriptor();
+ method public java.io.FileDescriptor getFileDescriptor();
method protected java.net.InetAddress getInetAddress();
method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
method protected int getLocalPort();
@@ -49081,6 +49373,11 @@
method public abstract java.net.SocketImpl createSocketImpl();
}
+ public abstract interface SocketOption {
+ method public abstract java.lang.String name();
+ method public abstract java.lang.Class<T> type();
+ }
+
public abstract interface SocketOptions {
method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -49102,21 +49399,46 @@
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
public class SocketTimeoutException extends java.io.InterruptedIOException {
- ctor public SocketTimeoutException();
ctor public SocketTimeoutException(java.lang.String);
+ ctor public SocketTimeoutException();
+ ctor public SocketTimeoutException(java.lang.Throwable);
+ ctor public SocketTimeoutException(java.lang.String, java.lang.Throwable);
+ }
+
+ public final class StandardProtocolFamily extends java.lang.Enum implements java.net.ProtocolFamily {
+ method public static java.net.StandardProtocolFamily valueOf(java.lang.String);
+ method public static final java.net.StandardProtocolFamily[] values();
+ enum_constant public static final java.net.StandardProtocolFamily INET;
+ enum_constant public static final java.net.StandardProtocolFamily INET6;
+ }
+
+ public final class StandardSocketOptions {
+ field public static final java.net.SocketOption<java.net.NetworkInterface> IP_MULTICAST_IF;
+ field public static final java.net.SocketOption<java.lang.Boolean> IP_MULTICAST_LOOP;
+ field public static final java.net.SocketOption<java.lang.Integer> IP_MULTICAST_TTL;
+ field public static final java.net.SocketOption<java.lang.Integer> IP_TOS;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_BROADCAST;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_KEEPALIVE;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_LINGER;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_RCVBUF;
+ field public static final java.net.SocketOption<java.lang.Boolean> SO_REUSEADDR;
+ field public static final java.net.SocketOption<java.lang.Integer> SO_SNDBUF;
+ field public static final java.net.SocketOption<java.lang.Boolean> TCP_NODELAY;
}
public final class URI implements java.lang.Comparable java.io.Serializable {
ctor public URI(java.lang.String) throws java.net.URISyntaxException;
- ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
- ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
+ ctor public URI(java.lang.String, java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
+ ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
method public int compareTo(java.net.URI);
method public static java.net.URI create(java.lang.String);
method public java.lang.String getAuthority();
@@ -49154,12 +49476,12 @@
}
public final class URL implements java.io.Serializable {
+ ctor public URL(java.lang.String, java.lang.String, int, java.lang.String) throws java.net.MalformedURLException;
+ ctor public URL(java.lang.String, java.lang.String, java.lang.String) throws java.net.MalformedURLException;
+ ctor public URL(java.lang.String, java.lang.String, int, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
ctor public URL(java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String) throws java.net.MalformedURLException;
ctor public URL(java.net.URL, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, java.lang.String) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, int, java.lang.String) throws java.net.MalformedURLException;
- ctor public URL(java.lang.String, java.lang.String, int, java.lang.String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
method public java.lang.String getAuthority();
method public final java.lang.Object getContent() throws java.io.IOException;
method public final java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
@@ -49178,22 +49500,24 @@
method public boolean sameFile(java.net.URL);
method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public static synchronized void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
+ method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
method public java.lang.String toExternalForm();
method public java.net.URI toURI() throws java.net.URISyntaxException;
+ method public java.net.URI toURILenient() throws java.net.URISyntaxException;
}
- public class URLClassLoader extends java.security.SecureClassLoader {
- ctor public URLClassLoader(java.net.URL[]);
+ public class URLClassLoader extends java.security.SecureClassLoader implements java.io.Closeable {
ctor public URLClassLoader(java.net.URL[], java.lang.ClassLoader);
+ ctor public URLClassLoader(java.net.URL[]);
ctor public URLClassLoader(java.net.URL[], java.lang.ClassLoader, java.net.URLStreamHandlerFactory);
method protected void addURL(java.net.URL);
+ method public void close() throws java.io.IOException;
method protected java.lang.Package definePackage(java.lang.String, java.util.jar.Manifest, java.net.URL) throws java.lang.IllegalArgumentException;
method public java.net.URL findResource(java.lang.String);
method public java.util.Enumeration<java.net.URL> findResources(java.lang.String) throws java.io.IOException;
method public java.net.URL[] getURLs();
- method public static java.net.URLClassLoader newInstance(java.net.URL[]);
method public static java.net.URLClassLoader newInstance(java.net.URL[], java.lang.ClassLoader);
+ method public static java.net.URLClassLoader newInstance(java.net.URL[]);
}
public abstract class URLConnection {
@@ -49206,6 +49530,7 @@
method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public java.lang.String getContentEncoding();
method public int getContentLength();
+ method public long getContentLengthLong();
method public java.lang.String getContentType();
method public long getDate();
method public static boolean getDefaultAllowUserInteraction();
@@ -49214,12 +49539,13 @@
method public boolean getDoInput();
method public boolean getDoOutput();
method public long getExpiration();
- method public static java.net.FileNameMap getFileNameMap();
- method public java.lang.String getHeaderField(int);
+ method public static synchronized java.net.FileNameMap getFileNameMap();
method public java.lang.String getHeaderField(java.lang.String);
+ method public java.lang.String getHeaderField(int);
method public long getHeaderFieldDate(java.lang.String, long);
method public int getHeaderFieldInt(java.lang.String, int);
method public java.lang.String getHeaderFieldKey(int);
+ method public long getHeaderFieldLong(java.lang.String, long);
method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
method public long getIfModifiedSince();
method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -49270,15 +49596,15 @@
ctor public URLStreamHandler();
method protected boolean equals(java.net.URL, java.net.URL);
method protected int getDefaultPort();
- method protected java.net.InetAddress getHostAddress(java.net.URL);
+ method protected synchronized java.net.InetAddress getHostAddress(java.net.URL);
method protected int hashCode(java.net.URL);
method protected boolean hostsEqual(java.net.URL, java.net.URL);
method protected abstract java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
method protected java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
method protected void parseURL(java.net.URL, java.lang.String, int, int);
method protected boolean sameFile(java.net.URL, java.net.URL);
- method protected deprecated void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method protected deprecated void setURL(java.net.URL, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
method protected java.lang.String toExternalForm(java.net.URL);
}
@@ -49287,8 +49613,8 @@
}
public class UnknownHostException extends java.io.IOException {
- ctor public UnknownHostException();
ctor public UnknownHostException(java.lang.String);
+ ctor public UnknownHostException();
}
public class UnknownServiceException extends java.io.IOException {
@@ -49344,9 +49670,9 @@
method public int compareTo(java.nio.ByteBuffer);
method public abstract java.nio.ByteBuffer duplicate();
method public abstract byte get();
- method public java.nio.ByteBuffer get(byte[]);
- method public java.nio.ByteBuffer get(byte[], int, int);
method public abstract byte get(int);
+ method public java.nio.ByteBuffer get(byte[], int, int);
+ method public java.nio.ByteBuffer get(byte[]);
method public abstract char getChar();
method public abstract char getChar(int);
method public abstract double getDouble();
@@ -49364,10 +49690,10 @@
method public final java.nio.ByteOrder order();
method public final java.nio.ByteBuffer order(java.nio.ByteOrder);
method public abstract java.nio.ByteBuffer put(byte);
- method public final java.nio.ByteBuffer put(byte[]);
- method public java.nio.ByteBuffer put(byte[], int, int);
- method public java.nio.ByteBuffer put(java.nio.ByteBuffer);
method public abstract java.nio.ByteBuffer put(int, byte);
+ method public java.nio.ByteBuffer put(java.nio.ByteBuffer);
+ method public java.nio.ByteBuffer put(byte[], int, int);
+ method public final java.nio.ByteBuffer put(byte[]);
method public abstract java.nio.ByteBuffer putChar(char);
method public abstract java.nio.ByteBuffer putChar(int, char);
method public abstract java.nio.ByteBuffer putDouble(double);
@@ -49381,8 +49707,8 @@
method public abstract java.nio.ByteBuffer putShort(short);
method public abstract java.nio.ByteBuffer putShort(int, short);
method public abstract java.nio.ByteBuffer slice();
- method public static java.nio.ByteBuffer wrap(byte[]);
method public static java.nio.ByteBuffer wrap(byte[], int, int);
+ method public static java.nio.ByteBuffer wrap(byte[]);
}
public final class ByteOrder {
@@ -49393,9 +49719,9 @@
public abstract class CharBuffer extends java.nio.Buffer implements java.lang.Appendable java.lang.CharSequence java.lang.Comparable java.lang.Readable {
method public static java.nio.CharBuffer allocate(int);
- method public java.nio.CharBuffer append(char);
method public java.nio.CharBuffer append(java.lang.CharSequence);
method public java.nio.CharBuffer append(java.lang.CharSequence, int, int);
+ method public java.nio.CharBuffer append(char);
method public final char[] array();
method public final int arrayOffset();
method public abstract java.nio.CharBuffer asReadOnlyBuffer();
@@ -49404,27 +49730,27 @@
method public int compareTo(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer duplicate();
method public abstract char get();
- method public java.nio.CharBuffer get(char[]);
- method public java.nio.CharBuffer get(char[], int, int);
method public abstract char get(int);
+ method public java.nio.CharBuffer get(char[], int, int);
+ method public java.nio.CharBuffer get(char[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public final int length();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.CharBuffer put(char);
- method public final java.nio.CharBuffer put(char[]);
- method public java.nio.CharBuffer put(char[], int, int);
- method public java.nio.CharBuffer put(java.nio.CharBuffer);
method public abstract java.nio.CharBuffer put(int, char);
- method public final java.nio.CharBuffer put(java.lang.String);
+ method public java.nio.CharBuffer put(java.nio.CharBuffer);
+ method public java.nio.CharBuffer put(char[], int, int);
+ method public final java.nio.CharBuffer put(char[]);
method public java.nio.CharBuffer put(java.lang.String, int, int);
+ method public final java.nio.CharBuffer put(java.lang.String);
method public int read(java.nio.CharBuffer) throws java.io.IOException;
method public abstract java.nio.CharBuffer slice();
method public abstract java.nio.CharBuffer subSequence(int, int);
- method public static java.nio.CharBuffer wrap(char[]);
method public static java.nio.CharBuffer wrap(char[], int, int);
- method public static java.nio.CharBuffer wrap(java.lang.CharSequence);
+ method public static java.nio.CharBuffer wrap(char[]);
method public static java.nio.CharBuffer wrap(java.lang.CharSequence, int, int);
+ method public static java.nio.CharBuffer wrap(java.lang.CharSequence);
}
public abstract class DoubleBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -49436,20 +49762,20 @@
method public int compareTo(java.nio.DoubleBuffer);
method public abstract java.nio.DoubleBuffer duplicate();
method public abstract double get();
- method public java.nio.DoubleBuffer get(double[]);
- method public java.nio.DoubleBuffer get(double[], int, int);
method public abstract double get(int);
+ method public java.nio.DoubleBuffer get(double[], int, int);
+ method public java.nio.DoubleBuffer get(double[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.DoubleBuffer put(double);
- method public final java.nio.DoubleBuffer put(double[]);
- method public java.nio.DoubleBuffer put(double[], int, int);
- method public java.nio.DoubleBuffer put(java.nio.DoubleBuffer);
method public abstract java.nio.DoubleBuffer put(int, double);
+ method public java.nio.DoubleBuffer put(java.nio.DoubleBuffer);
+ method public java.nio.DoubleBuffer put(double[], int, int);
+ method public final java.nio.DoubleBuffer put(double[]);
method public abstract java.nio.DoubleBuffer slice();
- method public static java.nio.DoubleBuffer wrap(double[]);
method public static java.nio.DoubleBuffer wrap(double[], int, int);
+ method public static java.nio.DoubleBuffer wrap(double[]);
}
public abstract class FloatBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -49461,20 +49787,20 @@
method public int compareTo(java.nio.FloatBuffer);
method public abstract java.nio.FloatBuffer duplicate();
method public abstract float get();
- method public java.nio.FloatBuffer get(float[]);
- method public java.nio.FloatBuffer get(float[], int, int);
method public abstract float get(int);
+ method public java.nio.FloatBuffer get(float[], int, int);
+ method public java.nio.FloatBuffer get(float[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.FloatBuffer put(float);
- method public final java.nio.FloatBuffer put(float[]);
- method public java.nio.FloatBuffer put(float[], int, int);
- method public java.nio.FloatBuffer put(java.nio.FloatBuffer);
method public abstract java.nio.FloatBuffer put(int, float);
+ method public java.nio.FloatBuffer put(java.nio.FloatBuffer);
+ method public java.nio.FloatBuffer put(float[], int, int);
+ method public final java.nio.FloatBuffer put(float[]);
method public abstract java.nio.FloatBuffer slice();
- method public static java.nio.FloatBuffer wrap(float[]);
method public static java.nio.FloatBuffer wrap(float[], int, int);
+ method public static java.nio.FloatBuffer wrap(float[]);
}
public abstract class IntBuffer extends java.nio.Buffer implements java.lang.Comparable {
@@ -49486,20 +49812,20 @@
method public int compareTo(java.nio.IntBuffer);
method public abstract java.nio.IntBuffer duplicate();
method public abstract int get();
- method public java.nio.IntBuffer get(int[]);
- method public java.nio.IntBuffer get(int[], int, int);
method public abstract int get(int);
+ method public java.nio.IntBuffer get(int[], int, int);
+ method public java.nio.IntBuffer get(int[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.IntBuffer put(int);
- method public final java.nio.IntBuffer put(int[]);
- method public java.nio.IntBuffer put(int[], int, int);
- method public java.nio.IntBuffer put(java.nio.IntBuffer);
method public abstract java.nio.IntBuffer put(int, int);
+ method public java.nio.IntBuffer put(java.nio.IntBuffer);
+ method public java.nio.IntBuffer put(int[], int, int);
+ method public final java.nio.IntBuffer put(int[]);
method public abstract java.nio.IntBuffer slice();
- method public static java.nio.IntBuffer wrap(int[]);
method public static java.nio.IntBuffer wrap(int[], int, int);
+ method public static java.nio.IntBuffer wrap(int[]);
}
public class InvalidMarkException extends java.lang.IllegalStateException {
@@ -49515,20 +49841,20 @@
method public int compareTo(java.nio.LongBuffer);
method public abstract java.nio.LongBuffer duplicate();
method public abstract long get();
- method public java.nio.LongBuffer get(long[]);
- method public java.nio.LongBuffer get(long[], int, int);
method public abstract long get(int);
+ method public java.nio.LongBuffer get(long[], int, int);
+ method public java.nio.LongBuffer get(long[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.LongBuffer put(long);
- method public final java.nio.LongBuffer put(long[]);
- method public java.nio.LongBuffer put(long[], int, int);
- method public java.nio.LongBuffer put(java.nio.LongBuffer);
method public abstract java.nio.LongBuffer put(int, long);
+ method public java.nio.LongBuffer put(java.nio.LongBuffer);
+ method public java.nio.LongBuffer put(long[], int, int);
+ method public final java.nio.LongBuffer put(long[]);
method public abstract java.nio.LongBuffer slice();
- method public static java.nio.LongBuffer wrap(long[]);
method public static java.nio.LongBuffer wrap(long[], int, int);
+ method public static java.nio.LongBuffer wrap(long[]);
}
public abstract class MappedByteBuffer extends java.nio.ByteBuffer {
@@ -49550,34 +49876,119 @@
method public int compareTo(java.nio.ShortBuffer);
method public abstract java.nio.ShortBuffer duplicate();
method public abstract short get();
- method public java.nio.ShortBuffer get(short[]);
- method public java.nio.ShortBuffer get(short[], int, int);
method public abstract short get(int);
+ method public java.nio.ShortBuffer get(short[], int, int);
+ method public java.nio.ShortBuffer get(short[]);
method public final boolean hasArray();
method public abstract boolean isDirect();
method public abstract java.nio.ByteOrder order();
method public abstract java.nio.ShortBuffer put(short);
- method public final java.nio.ShortBuffer put(short[]);
- method public java.nio.ShortBuffer put(short[], int, int);
- method public java.nio.ShortBuffer put(java.nio.ShortBuffer);
method public abstract java.nio.ShortBuffer put(int, short);
+ method public java.nio.ShortBuffer put(java.nio.ShortBuffer);
+ method public java.nio.ShortBuffer put(short[], int, int);
+ method public final java.nio.ShortBuffer put(short[]);
method public abstract java.nio.ShortBuffer slice();
- method public static java.nio.ShortBuffer wrap(short[]);
method public static java.nio.ShortBuffer wrap(short[], int, int);
+ method public static java.nio.ShortBuffer wrap(short[]);
}
}
package java.nio.channels {
+ public class AcceptPendingException extends java.lang.IllegalStateException {
+ ctor public AcceptPendingException();
+ }
+
+ public class AlreadyBoundException extends java.lang.IllegalStateException {
+ ctor public AlreadyBoundException();
+ }
+
public class AlreadyConnectedException extends java.lang.IllegalStateException {
ctor public AlreadyConnectedException();
}
+ public abstract interface AsynchronousByteChannel implements java.nio.channels.AsynchronousChannel {
+ method public abstract void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
+ method public abstract void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
+ }
+
+ public abstract interface AsynchronousChannel implements java.nio.channels.Channel {
+ method public abstract void close() throws java.io.IOException;
+ }
+
+ public abstract class AsynchronousChannelGroup {
+ ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public abstract boolean isShutdown();
+ method public abstract boolean isTerminated();
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract void shutdown();
+ method public abstract void shutdownNow() throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
+ }
+
public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
ctor public AsynchronousCloseException();
}
+ public abstract class AsynchronousFileChannel implements java.nio.channels.AsynchronousChannel {
+ ctor protected AsynchronousFileChannel();
+ method public abstract void force(boolean) throws java.io.IOException;
+ method public abstract void lock(long, long, boolean, A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock, ? super A>);
+ method public final void lock(A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock, ? super A>);
+ method public abstract java.util.concurrent.Future<java.nio.channels.FileLock> lock(long, long, boolean);
+ method public final java.util.concurrent.Future<java.nio.channels.FileLock> lock();
+ method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public abstract void read(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer, long);
+ method public abstract long size() throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousFileChannel truncate(long) throws java.io.IOException;
+ method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
+ method public abstract void write(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer, long);
+ }
+
+ public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
+ ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel, ? super A>);
+ method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
+ method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ }
+
+ public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
+ ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+ method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
+ method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+ method public abstract void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public final void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
+ method public abstract void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
+ method public abstract java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
+ method public abstract void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public final void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer, ? super A>);
+ method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
+ method public abstract void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long, ? super A>);
+ }
+
public abstract interface ByteChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
}
@@ -49594,7 +50005,9 @@
method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
+ method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
+ method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.lang.String);
method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
@@ -49613,50 +50026,61 @@
ctor public ClosedSelectorException();
}
+ public abstract interface CompletionHandler {
+ method public abstract void completed(V, A);
+ method public abstract void failed(java.lang.Throwable, A);
+ }
+
public class ConnectionPendingException extends java.lang.IllegalStateException {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
+ method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
method public abstract boolean isConnected();
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
+ method public static java.nio.channels.DatagramChannel open(java.net.ProtocolFamily) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.DatagramSocket socket();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
- public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
ctor protected FileChannel();
method public abstract void force(boolean) throws java.io.IOException;
- method public final java.nio.channels.FileLock lock() throws java.io.IOException;
method public abstract java.nio.channels.FileLock lock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock lock() throws java.io.IOException;
method public abstract java.nio.MappedByteBuffer map(java.nio.channels.FileChannel.MapMode, long, long) throws java.io.IOException;
+ method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
method public abstract long position() throws java.io.IOException;
method public abstract java.nio.channels.FileChannel position(long) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract int read(java.nio.ByteBuffer, long) throws java.io.IOException;
- method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract int read(java.nio.ByteBuffer, long) throws java.io.IOException;
method public abstract long size() throws java.io.IOException;
method public abstract long transferFrom(java.nio.channels.ReadableByteChannel, long, long) throws java.io.IOException;
method public abstract long transferTo(long, long, java.nio.channels.WritableByteChannel) throws java.io.IOException;
method public abstract java.nio.channels.FileChannel truncate(long) throws java.io.IOException;
- method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+ method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract int write(java.nio.ByteBuffer, long) throws java.io.IOException;
- method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract int write(java.nio.ByteBuffer, long) throws java.io.IOException;
}
public static class FileChannel.MapMode {
@@ -49667,6 +50091,8 @@
public abstract class FileLock implements java.lang.AutoCloseable {
ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
+ ctor protected FileLock(java.nio.channels.AsynchronousFileChannel, long, long, boolean);
+ method public java.nio.channels.Channel acquiredBy();
method public final java.nio.channels.FileChannel channel();
method public final void close() throws java.io.IOException;
method public final boolean isShared();
@@ -49683,22 +50109,56 @@
}
public abstract interface GatheringByteChannel implements java.nio.channels.WritableByteChannel {
- method public abstract long write(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public abstract long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
public class IllegalBlockingModeException extends java.lang.IllegalStateException {
ctor public IllegalBlockingModeException();
}
+ public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
+ ctor public IllegalChannelGroupException();
+ }
+
public class IllegalSelectorException extends java.lang.IllegalArgumentException {
ctor public IllegalSelectorException();
}
+ public class InterruptedByTimeoutException extends java.io.IOException {
+ ctor public InterruptedByTimeoutException();
+ }
+
public abstract interface InterruptibleChannel implements java.nio.channels.Channel {
method public abstract void close() throws java.io.IOException;
}
+ public abstract class MembershipKey {
+ ctor protected MembershipKey();
+ method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.MulticastChannel channel();
+ method public abstract void drop();
+ method public abstract java.net.InetAddress group();
+ method public abstract boolean isValid();
+ method public abstract java.net.NetworkInterface networkInterface();
+ method public abstract java.net.InetAddress sourceAddress();
+ method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
+ }
+
+ public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
+ }
+
+ public abstract interface NetworkChannel implements java.nio.channels.Channel {
+ method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
+ method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
+ }
+
public class NoConnectionPendingException extends java.lang.IllegalStateException {
ctor public NoConnectionPendingException();
}
@@ -49740,13 +50200,26 @@
method public final int validOps();
}
+ public class ReadPendingException extends java.lang.IllegalStateException {
+ ctor public ReadPendingException();
+ }
+
public abstract interface ReadableByteChannel implements java.nio.channels.Channel {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
}
public abstract interface ScatteringByteChannel implements java.nio.channels.ReadableByteChannel {
- method public abstract long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+ method public abstract long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ }
+
+ public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
+ method public abstract long position() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
+ method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
+ method public abstract long size() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
+ method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
@@ -49757,8 +50230,8 @@
method public abstract boolean isRegistered();
method public abstract java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
method public abstract java.nio.channels.spi.SelectorProvider provider();
- method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int) throws java.nio.channels.ClosedChannelException;
method public abstract java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, java.lang.Object) throws java.nio.channels.ClosedChannelException;
+ method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int) throws java.nio.channels.ClosedChannelException;
method public abstract int validOps();
}
@@ -49790,37 +50263,49 @@
method public abstract java.util.Set<java.nio.channels.SelectionKey> keys();
method public static java.nio.channels.Selector open() throws java.io.IOException;
method public abstract java.nio.channels.spi.SelectorProvider provider();
- method public abstract int select() throws java.io.IOException;
method public abstract int select(long) throws java.io.IOException;
+ method public abstract int select() throws java.io.IOException;
method public abstract int selectNow() throws java.io.IOException;
method public abstract java.util.Set<java.nio.channels.SelectionKey> selectedKeys();
method public abstract java.nio.channels.Selector wakeup();
}
- public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
+ public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
+ method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
+ method public abstract java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.ServerSocket socket();
method public final int validOps();
}
- public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
+ ctor public ShutdownChannelGroupException();
+ }
+
+ public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
+ method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean finishConnect() throws java.io.IOException;
+ method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
method public abstract boolean isConnected();
method public abstract boolean isConnectionPending();
method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
method public static java.nio.channels.SocketChannel open(java.net.SocketAddress) throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel shutdownInput() throws java.io.IOException;
+ method public abstract java.nio.channels.SocketChannel shutdownOutput() throws java.io.IOException;
method public abstract java.net.Socket socket();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
- method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
public class UnresolvedAddressException extends java.lang.IllegalArgumentException {
@@ -49835,6 +50320,10 @@
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
}
+ public class WritePendingException extends java.lang.IllegalStateException {
+ ctor public WritePendingException();
+ }
+
}
package java.nio.channels.spi {
@@ -49845,19 +50334,19 @@
method public final void close() throws java.io.IOException;
method protected final void end(boolean) throws java.nio.channels.AsynchronousCloseException;
method protected abstract void implCloseChannel() throws java.io.IOException;
- method public final synchronized boolean isOpen();
+ method public final boolean isOpen();
}
public abstract class AbstractSelectableChannel extends java.nio.channels.SelectableChannel {
ctor protected AbstractSelectableChannel(java.nio.channels.spi.SelectorProvider);
method public final java.lang.Object blockingLock();
method public final java.nio.channels.SelectableChannel configureBlocking(boolean) throws java.io.IOException;
- method protected final synchronized void implCloseChannel() throws java.io.IOException;
+ method protected final void implCloseChannel() throws java.io.IOException;
method protected abstract void implCloseSelectableChannel() throws java.io.IOException;
method protected abstract void implConfigureBlocking(boolean) throws java.io.IOException;
method public final boolean isBlocking();
- method public final synchronized boolean isRegistered();
- method public final synchronized java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
+ method public final boolean isRegistered();
+ method public final java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
method public final java.nio.channels.spi.SelectorProvider provider();
method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, java.lang.Object) throws java.nio.channels.ClosedChannelException;
}
@@ -49881,15 +50370,25 @@
method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object);
}
+ public abstract class AsynchronousChannelProvider {
+ ctor protected AsynchronousChannelProvider();
+ method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+ method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
+ }
+
public abstract class SelectorProvider {
ctor protected SelectorProvider();
method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel openDatagramChannel() throws java.io.IOException;
+ method public abstract java.nio.channels.DatagramChannel openDatagramChannel(java.net.ProtocolFamily) throws java.io.IOException;
method public abstract java.nio.channels.Pipe openPipe() throws java.io.IOException;
method public abstract java.nio.channels.spi.AbstractSelector openSelector() throws java.io.IOException;
method public abstract java.nio.channels.ServerSocketChannel openServerSocketChannel() throws java.io.IOException;
method public abstract java.nio.channels.SocketChannel openSocketChannel() throws java.io.IOException;
- method public static synchronized java.nio.channels.spi.SelectorProvider provider();
+ method public static java.nio.channels.spi.SelectorProvider provider();
}
}
@@ -49928,8 +50427,8 @@
ctor protected CharsetDecoder(java.nio.charset.Charset, float, float);
method public final float averageCharsPerByte();
method public final java.nio.charset.Charset charset();
- method public final java.nio.CharBuffer decode(java.nio.ByteBuffer) throws java.nio.charset.CharacterCodingException;
method public final java.nio.charset.CoderResult decode(java.nio.ByteBuffer, java.nio.CharBuffer, boolean);
+ method public final java.nio.CharBuffer decode(java.nio.ByteBuffer) throws java.nio.charset.CharacterCodingException;
method protected abstract java.nio.charset.CoderResult decodeLoop(java.nio.ByteBuffer, java.nio.CharBuffer);
method public java.nio.charset.Charset detectedCharset();
method public final java.nio.charset.CoderResult flush(java.nio.CharBuffer);
@@ -49951,14 +50450,14 @@
}
public abstract class CharsetEncoder {
- ctor protected CharsetEncoder(java.nio.charset.Charset, float, float);
ctor protected CharsetEncoder(java.nio.charset.Charset, float, float, byte[]);
+ ctor protected CharsetEncoder(java.nio.charset.Charset, float, float);
method public final float averageBytesPerChar();
method public boolean canEncode(char);
method public boolean canEncode(java.lang.CharSequence);
method public final java.nio.charset.Charset charset();
- method public final java.nio.ByteBuffer encode(java.nio.CharBuffer) throws java.nio.charset.CharacterCodingException;
method public final java.nio.charset.CoderResult encode(java.nio.CharBuffer, java.nio.ByteBuffer, boolean);
+ method public final java.nio.ByteBuffer encode(java.nio.CharBuffer) throws java.nio.charset.CharacterCodingException;
method protected abstract java.nio.charset.CoderResult encodeLoop(java.nio.CharBuffer, java.nio.ByteBuffer);
method public final java.nio.charset.CoderResult flush(java.nio.ByteBuffer);
method protected java.nio.charset.CoderResult implFlush(java.nio.ByteBuffer);
@@ -49987,10 +50486,10 @@
method public boolean isOverflow();
method public boolean isUnderflow();
method public boolean isUnmappable();
- method public int length() throws java.lang.UnsupportedOperationException;
- method public static synchronized java.nio.charset.CoderResult malformedForLength(int) throws java.lang.IllegalArgumentException;
- method public void throwException() throws java.nio.BufferOverflowException, java.nio.BufferUnderflowException, java.nio.charset.CharacterCodingException, java.nio.charset.MalformedInputException, java.nio.charset.UnmappableCharacterException;
- method public static synchronized java.nio.charset.CoderResult unmappableForLength(int) throws java.lang.IllegalArgumentException;
+ method public int length();
+ method public static java.nio.charset.CoderResult malformedForLength(int);
+ method public void throwException() throws java.nio.charset.CharacterCodingException;
+ method public static java.nio.charset.CoderResult unmappableForLength(int);
field public static final java.nio.charset.CoderResult OVERFLOW;
field public static final java.nio.charset.CoderResult UNDERFLOW;
}
@@ -50042,11 +50541,597 @@
}
+package java.nio.file {
+
+ public class AccessDeniedException extends java.nio.file.FileSystemException {
+ ctor public AccessDeniedException(java.lang.String);
+ ctor public AccessDeniedException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public final class AccessMode extends java.lang.Enum {
+ method public static java.nio.file.AccessMode valueOf(java.lang.String);
+ method public static final java.nio.file.AccessMode[] values();
+ enum_constant public static final java.nio.file.AccessMode EXECUTE;
+ enum_constant public static final java.nio.file.AccessMode READ;
+ enum_constant public static final java.nio.file.AccessMode WRITE;
+ }
+
+ public class AtomicMoveNotSupportedException extends java.nio.file.FileSystemException {
+ ctor public AtomicMoveNotSupportedException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public class ClosedDirectoryStreamException extends java.lang.IllegalStateException {
+ ctor public ClosedDirectoryStreamException();
+ }
+
+ public class ClosedFileSystemException extends java.lang.IllegalStateException {
+ ctor public ClosedFileSystemException();
+ }
+
+ public class ClosedWatchServiceException extends java.lang.IllegalStateException {
+ ctor public ClosedWatchServiceException();
+ }
+
+ public abstract interface CopyOption {
+ }
+
+ public final class DirectoryIteratorException extends java.util.ConcurrentModificationException {
+ ctor public DirectoryIteratorException(java.io.IOException);
+ }
+
+ public class DirectoryNotEmptyException extends java.nio.file.FileSystemException {
+ ctor public DirectoryNotEmptyException(java.lang.String);
+ }
+
+ public abstract interface DirectoryStream implements java.io.Closeable java.lang.Iterable {
+ method public abstract java.util.Iterator<T> iterator();
+ }
+
+ public static abstract interface DirectoryStream.Filter {
+ method public abstract boolean accept(T) throws java.io.IOException;
+ }
+
+ public class FileAlreadyExistsException extends java.nio.file.FileSystemException {
+ ctor public FileAlreadyExistsException(java.lang.String);
+ ctor public FileAlreadyExistsException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract class FileStore {
+ ctor protected FileStore();
+ method public abstract java.lang.Object getAttribute(java.lang.String) throws java.io.IOException;
+ method public abstract V getFileStoreAttributeView(java.lang.Class<V>);
+ method public abstract long getTotalSpace() throws java.io.IOException;
+ method public abstract long getUnallocatedSpace() throws java.io.IOException;
+ method public abstract long getUsableSpace() throws java.io.IOException;
+ method public abstract boolean isReadOnly();
+ method public abstract java.lang.String name();
+ method public abstract boolean supportsFileAttributeView(java.lang.Class<? extends java.nio.file.attribute.FileAttributeView>);
+ method public abstract boolean supportsFileAttributeView(java.lang.String);
+ method public abstract java.lang.String type();
+ }
+
+ public abstract class FileSystem implements java.io.Closeable {
+ ctor protected FileSystem();
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.lang.Iterable<java.nio.file.FileStore> getFileStores();
+ method public abstract java.nio.file.Path getPath(java.lang.String, java.lang.String...);
+ method public abstract java.nio.file.PathMatcher getPathMatcher(java.lang.String);
+ method public abstract java.lang.Iterable<java.nio.file.Path> getRootDirectories();
+ method public abstract java.lang.String getSeparator();
+ method public abstract java.nio.file.attribute.UserPrincipalLookupService getUserPrincipalLookupService();
+ method public abstract boolean isOpen();
+ method public abstract boolean isReadOnly();
+ method public abstract java.nio.file.WatchService newWatchService() throws java.io.IOException;
+ method public abstract java.nio.file.spi.FileSystemProvider provider();
+ method public abstract java.util.Set<java.lang.String> supportedFileAttributeViews();
+ }
+
+ public class FileSystemAlreadyExistsException extends java.lang.RuntimeException {
+ ctor public FileSystemAlreadyExistsException();
+ ctor public FileSystemAlreadyExistsException(java.lang.String);
+ }
+
+ public class FileSystemException extends java.io.IOException {
+ ctor public FileSystemException(java.lang.String);
+ ctor public FileSystemException(java.lang.String, java.lang.String, java.lang.String);
+ method public java.lang.String getFile();
+ method public java.lang.String getOtherFile();
+ method public java.lang.String getReason();
+ }
+
+ public class FileSystemLoopException extends java.nio.file.FileSystemException {
+ ctor public FileSystemLoopException(java.lang.String);
+ }
+
+ public class FileSystemNotFoundException extends java.lang.RuntimeException {
+ ctor public FileSystemNotFoundException();
+ ctor public FileSystemNotFoundException(java.lang.String);
+ }
+
+ public final class FileSystems {
+ method public static java.nio.file.FileSystem getDefault();
+ method public static java.nio.file.FileSystem getFileSystem(java.net.URI);
+ method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>, java.lang.ClassLoader) throws java.io.IOException;
+ method public static java.nio.file.FileSystem newFileSystem(java.nio.file.Path, java.lang.ClassLoader) throws java.io.IOException;
+ }
+
+ public final class FileVisitOption extends java.lang.Enum {
+ method public static java.nio.file.FileVisitOption valueOf(java.lang.String);
+ method public static final java.nio.file.FileVisitOption[] values();
+ enum_constant public static final java.nio.file.FileVisitOption FOLLOW_LINKS;
+ }
+
+ public final class FileVisitResult extends java.lang.Enum {
+ method public static java.nio.file.FileVisitResult valueOf(java.lang.String);
+ method public static final java.nio.file.FileVisitResult[] values();
+ enum_constant public static final java.nio.file.FileVisitResult CONTINUE;
+ enum_constant public static final java.nio.file.FileVisitResult SKIP_SIBLINGS;
+ enum_constant public static final java.nio.file.FileVisitResult SKIP_SUBTREE;
+ enum_constant public static final java.nio.file.FileVisitResult TERMINATE;
+ }
+
+ public abstract interface FileVisitor {
+ method public abstract java.nio.file.FileVisitResult postVisitDirectory(T, java.io.IOException) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult preVisitDirectory(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult visitFile(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+ method public abstract java.nio.file.FileVisitResult visitFileFailed(T, java.io.IOException) throws java.io.IOException;
+ }
+
+ public final class Files {
+ method public static java.nio.file.Path copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static long copy(java.io.InputStream, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static long copy(java.nio.file.Path, java.io.OutputStream) throws java.io.IOException;
+ method public static java.nio.file.Path createDirectories(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createFile(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempDirectory(java.nio.file.Path, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempDirectory(java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempFile(java.nio.file.Path, java.lang.String, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.file.Path createTempFile(java.lang.String, java.lang.String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static void delete(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean exists(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static java.lang.Object getAttribute(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static V getFileAttributeView(java.nio.file.Path, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public static java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.attribute.FileTime getLastModifiedTime(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.attribute.UserPrincipal getOwner(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> getPosixFilePermissions(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static boolean isDirectory(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static boolean isExecutable(java.nio.file.Path);
+ method public static boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+ method public static boolean isReadable(java.nio.file.Path);
+ method public static boolean isRegularFile(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public static boolean isSymbolicLink(java.nio.file.Path);
+ method public static boolean isWritable(java.nio.file.Path);
+ method public static java.nio.file.Path move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public static java.io.BufferedReader newBufferedReader(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+ method public static java.io.BufferedWriter newBufferedWriter(java.nio.file.Path, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.lang.String) throws java.io.IOException;
+ method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static boolean notExists(java.nio.file.Path, java.nio.file.LinkOption...);
+ method public static java.lang.String probeContentType(java.nio.file.Path) throws java.io.IOException;
+ method public static byte[] readAllBytes(java.nio.file.Path) throws java.io.IOException;
+ method public static java.util.List<java.lang.String> readAllLines(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+ method public static A readAttributes(java.nio.file.Path, java.lang.Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.util.Map<java.lang.String, java.lang.Object> readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public static java.nio.file.Path setLastModifiedTime(java.nio.file.Path, java.nio.file.attribute.FileTime) throws java.io.IOException;
+ method public static java.nio.file.Path setOwner(java.nio.file.Path, java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+ method public static java.nio.file.Path setPosixFilePermissions(java.nio.file.Path, java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+ method public static long size(java.nio.file.Path) throws java.io.IOException;
+ method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.util.Set<java.nio.file.FileVisitOption>, int, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+ method public static java.nio.file.Path write(java.nio.file.Path, byte[], java.nio.file.OpenOption...) throws java.io.IOException;
+ method public static java.nio.file.Path write(java.nio.file.Path, java.lang.Iterable<? extends java.lang.CharSequence>, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+ }
+
+ public class InvalidPathException extends java.lang.IllegalArgumentException {
+ ctor public InvalidPathException(java.lang.String, java.lang.String, int);
+ ctor public InvalidPathException(java.lang.String, java.lang.String);
+ method public int getIndex();
+ method public java.lang.String getInput();
+ method public java.lang.String getReason();
+ }
+
+ public final class LinkOption extends java.lang.Enum implements java.nio.file.CopyOption java.nio.file.OpenOption {
+ method public static java.nio.file.LinkOption valueOf(java.lang.String);
+ method public static final java.nio.file.LinkOption[] values();
+ enum_constant public static final java.nio.file.LinkOption NOFOLLOW_LINKS;
+ }
+
+ public final class LinkPermission extends java.security.BasicPermission {
+ ctor public LinkPermission(java.lang.String);
+ ctor public LinkPermission(java.lang.String, java.lang.String);
+ }
+
+ public class NoSuchFileException extends java.nio.file.FileSystemException {
+ ctor public NoSuchFileException(java.lang.String);
+ ctor public NoSuchFileException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public class NotDirectoryException extends java.nio.file.FileSystemException {
+ ctor public NotDirectoryException(java.lang.String);
+ }
+
+ public class NotLinkException extends java.nio.file.FileSystemException {
+ ctor public NotLinkException(java.lang.String);
+ ctor public NotLinkException(java.lang.String, java.lang.String, java.lang.String);
+ }
+
+ public abstract interface OpenOption {
+ }
+
+ public abstract interface Path implements java.lang.Comparable java.lang.Iterable java.nio.file.Watchable {
+ method public abstract int compareTo(java.nio.file.Path);
+ method public abstract boolean endsWith(java.nio.file.Path);
+ method public abstract boolean endsWith(java.lang.String);
+ method public abstract boolean equals(java.lang.Object);
+ method public abstract java.nio.file.Path getFileName();
+ method public abstract java.nio.file.FileSystem getFileSystem();
+ method public abstract java.nio.file.Path getName(int);
+ method public abstract int getNameCount();
+ method public abstract java.nio.file.Path getParent();
+ method public abstract java.nio.file.Path getRoot();
+ method public abstract int hashCode();
+ method public abstract boolean isAbsolute();
+ method public abstract java.util.Iterator<java.nio.file.Path> iterator();
+ method public abstract java.nio.file.Path normalize();
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>[], java.nio.file.WatchEvent.Modifier...) throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.Path relativize(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolve(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolve(java.lang.String);
+ method public abstract java.nio.file.Path resolveSibling(java.nio.file.Path);
+ method public abstract java.nio.file.Path resolveSibling(java.lang.String);
+ method public abstract boolean startsWith(java.nio.file.Path);
+ method public abstract boolean startsWith(java.lang.String);
+ method public abstract java.nio.file.Path subpath(int, int);
+ method public abstract java.nio.file.Path toAbsolutePath();
+ method public abstract java.io.File toFile();
+ method public abstract java.nio.file.Path toRealPath(java.nio.file.LinkOption...) throws java.io.IOException;
+ method public abstract java.lang.String toString();
+ method public abstract java.net.URI toUri();
+ }
+
+ public abstract interface PathMatcher {
+ method public abstract boolean matches(java.nio.file.Path);
+ }
+
+ public final class Paths {
+ method public static java.nio.file.Path get(java.lang.String, java.lang.String...);
+ method public static java.nio.file.Path get(java.net.URI);
+ }
+
+ public class ProviderMismatchException extends java.lang.IllegalArgumentException {
+ ctor public ProviderMismatchException();
+ ctor public ProviderMismatchException(java.lang.String);
+ }
+
+ public class ProviderNotFoundException extends java.lang.RuntimeException {
+ ctor public ProviderNotFoundException();
+ ctor public ProviderNotFoundException(java.lang.String);
+ }
+
+ public class ReadOnlyFileSystemException extends java.lang.UnsupportedOperationException {
+ ctor public ReadOnlyFileSystemException();
+ }
+
+ public abstract interface SecureDirectoryStream implements java.nio.file.DirectoryStream {
+ method public abstract void deleteDirectory(T) throws java.io.IOException;
+ method public abstract void deleteFile(T) throws java.io.IOException;
+ method public abstract V getFileAttributeView(java.lang.Class<V>);
+ method public abstract V getFileAttributeView(T, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public abstract void move(T, java.nio.file.SecureDirectoryStream<T>, T) throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel newByteChannel(T, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.SecureDirectoryStream<T> newDirectoryStream(T, java.nio.file.LinkOption...) throws java.io.IOException;
+ }
+
+ public final class StandardCopyOption extends java.lang.Enum implements java.nio.file.CopyOption {
+ method public static java.nio.file.StandardCopyOption valueOf(java.lang.String);
+ method public static final java.nio.file.StandardCopyOption[] values();
+ enum_constant public static final java.nio.file.StandardCopyOption ATOMIC_MOVE;
+ enum_constant public static final java.nio.file.StandardCopyOption COPY_ATTRIBUTES;
+ enum_constant public static final java.nio.file.StandardCopyOption REPLACE_EXISTING;
+ }
+
+ public final class StandardOpenOption extends java.lang.Enum implements java.nio.file.OpenOption {
+ method public static java.nio.file.StandardOpenOption valueOf(java.lang.String);
+ method public static final java.nio.file.StandardOpenOption[] values();
+ enum_constant public static final java.nio.file.StandardOpenOption APPEND;
+ enum_constant public static final java.nio.file.StandardOpenOption CREATE;
+ enum_constant public static final java.nio.file.StandardOpenOption CREATE_NEW;
+ enum_constant public static final java.nio.file.StandardOpenOption DELETE_ON_CLOSE;
+ enum_constant public static final java.nio.file.StandardOpenOption DSYNC;
+ enum_constant public static final java.nio.file.StandardOpenOption READ;
+ enum_constant public static final java.nio.file.StandardOpenOption SPARSE;
+ enum_constant public static final java.nio.file.StandardOpenOption SYNC;
+ enum_constant public static final java.nio.file.StandardOpenOption TRUNCATE_EXISTING;
+ enum_constant public static final java.nio.file.StandardOpenOption WRITE;
+ }
+
+ public final class StandardWatchEventKinds {
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_CREATE;
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_DELETE;
+ field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_MODIFY;
+ field public static final java.nio.file.WatchEvent.Kind<java.lang.Object> OVERFLOW;
+ }
+
+ public abstract interface WatchEvent {
+ method public abstract T context();
+ method public abstract int count();
+ method public abstract java.nio.file.WatchEvent.Kind<T> kind();
+ }
+
+ public static abstract interface WatchEvent.Kind {
+ method public abstract java.lang.String name();
+ method public abstract java.lang.Class<T> type();
+ }
+
+ public static abstract interface WatchEvent.Modifier {
+ method public abstract java.lang.String name();
+ }
+
+ public abstract interface WatchKey {
+ method public abstract void cancel();
+ method public abstract boolean isValid();
+ method public abstract java.util.List<java.nio.file.WatchEvent<?>> pollEvents();
+ method public abstract boolean reset();
+ method public abstract java.nio.file.Watchable watchable();
+ }
+
+ public abstract interface WatchService implements java.io.Closeable {
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey poll();
+ method public abstract java.nio.file.WatchKey poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public abstract java.nio.file.WatchKey take() throws java.lang.InterruptedException;
+ }
+
+ public abstract interface Watchable {
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>[], java.nio.file.WatchEvent.Modifier...) throws java.io.IOException;
+ method public abstract java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>...) throws java.io.IOException;
+ }
+
+}
+
+package java.nio.file.attribute {
+
+ public final class AclEntry {
+ method public java.util.Set<java.nio.file.attribute.AclEntryFlag> flags();
+ method public static java.nio.file.attribute.AclEntry.Builder newBuilder();
+ method public static java.nio.file.attribute.AclEntry.Builder newBuilder(java.nio.file.attribute.AclEntry);
+ method public java.util.Set<java.nio.file.attribute.AclEntryPermission> permissions();
+ method public java.nio.file.attribute.UserPrincipal principal();
+ method public java.nio.file.attribute.AclEntryType type();
+ }
+
+ public static final class AclEntry.Builder {
+ method public java.nio.file.attribute.AclEntry build();
+ method public java.nio.file.attribute.AclEntry.Builder setFlags(java.util.Set<java.nio.file.attribute.AclEntryFlag>);
+ method public java.nio.file.attribute.AclEntry.Builder setFlags(java.nio.file.attribute.AclEntryFlag...);
+ method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.util.Set<java.nio.file.attribute.AclEntryPermission>);
+ method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.nio.file.attribute.AclEntryPermission...);
+ method public java.nio.file.attribute.AclEntry.Builder setPrincipal(java.nio.file.attribute.UserPrincipal);
+ method public java.nio.file.attribute.AclEntry.Builder setType(java.nio.file.attribute.AclEntryType);
+ }
+
+ public final class AclEntryFlag extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryFlag valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryFlag[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag DIRECTORY_INHERIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag FILE_INHERIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag INHERIT_ONLY;
+ enum_constant public static final java.nio.file.attribute.AclEntryFlag NO_PROPAGATE_INHERIT;
+ }
+
+ public final class AclEntryPermission extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryPermission valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryPermission[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission APPEND_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE_CHILD;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission EXECUTE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ACL;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ATTRIBUTES;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_NAMED_ATTRS;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission SYNCHRONIZE;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ACL;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ATTRIBUTES;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_DATA;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_NAMED_ATTRS;
+ enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_OWNER;
+ field public static final java.nio.file.attribute.AclEntryPermission ADD_FILE;
+ field public static final java.nio.file.attribute.AclEntryPermission ADD_SUBDIRECTORY;
+ field public static final java.nio.file.attribute.AclEntryPermission LIST_DIRECTORY;
+ }
+
+ public final class AclEntryType extends java.lang.Enum {
+ method public static java.nio.file.attribute.AclEntryType valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.AclEntryType[] values();
+ enum_constant public static final java.nio.file.attribute.AclEntryType ALARM;
+ enum_constant public static final java.nio.file.attribute.AclEntryType ALLOW;
+ enum_constant public static final java.nio.file.attribute.AclEntryType AUDIT;
+ enum_constant public static final java.nio.file.attribute.AclEntryType DENY;
+ }
+
+ public abstract interface AclFileAttributeView implements java.nio.file.attribute.FileOwnerAttributeView {
+ method public abstract java.util.List<java.nio.file.attribute.AclEntry> getAcl() throws java.io.IOException;
+ method public abstract java.lang.String name();
+ method public abstract void setAcl(java.util.List<java.nio.file.attribute.AclEntry>) throws java.io.IOException;
+ }
+
+ public abstract interface AttributeView {
+ method public abstract java.lang.String name();
+ }
+
+ public abstract interface BasicFileAttributeView implements java.nio.file.attribute.FileAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.BasicFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setTimes(java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime) throws java.io.IOException;
+ }
+
+ public abstract interface BasicFileAttributes {
+ method public abstract java.nio.file.attribute.FileTime creationTime();
+ method public abstract java.lang.Object fileKey();
+ method public abstract boolean isDirectory();
+ method public abstract boolean isOther();
+ method public abstract boolean isRegularFile();
+ method public abstract boolean isSymbolicLink();
+ method public abstract java.nio.file.attribute.FileTime lastAccessTime();
+ method public abstract java.nio.file.attribute.FileTime lastModifiedTime();
+ method public abstract long size();
+ }
+
+ public abstract interface DosFileAttributeView implements java.nio.file.attribute.BasicFileAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.DosFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setArchive(boolean) throws java.io.IOException;
+ method public abstract void setHidden(boolean) throws java.io.IOException;
+ method public abstract void setReadOnly(boolean) throws java.io.IOException;
+ method public abstract void setSystem(boolean) throws java.io.IOException;
+ }
+
+ public abstract interface DosFileAttributes implements java.nio.file.attribute.BasicFileAttributes {
+ method public abstract boolean isArchive();
+ method public abstract boolean isHidden();
+ method public abstract boolean isReadOnly();
+ method public abstract boolean isSystem();
+ }
+
+ public abstract interface FileAttribute {
+ method public abstract java.lang.String name();
+ method public abstract T value();
+ }
+
+ public abstract interface FileAttributeView implements java.nio.file.attribute.AttributeView {
+ }
+
+ public abstract interface FileOwnerAttributeView implements java.nio.file.attribute.FileAttributeView {
+ method public abstract java.nio.file.attribute.UserPrincipal getOwner() throws java.io.IOException;
+ method public abstract java.lang.String name();
+ method public abstract void setOwner(java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+ }
+
+ public abstract interface FileStoreAttributeView implements java.nio.file.attribute.AttributeView {
+ }
+
+ public final class FileTime implements java.lang.Comparable {
+ method public int compareTo(java.nio.file.attribute.FileTime);
+ method public static java.nio.file.attribute.FileTime from(long, java.util.concurrent.TimeUnit);
+ method public static java.nio.file.attribute.FileTime fromMillis(long);
+ method public long to(java.util.concurrent.TimeUnit);
+ method public long toMillis();
+ }
+
+ public abstract interface GroupPrincipal implements java.nio.file.attribute.UserPrincipal {
+ }
+
+ public abstract interface PosixFileAttributeView implements java.nio.file.attribute.BasicFileAttributeView java.nio.file.attribute.FileOwnerAttributeView {
+ method public abstract java.lang.String name();
+ method public abstract java.nio.file.attribute.PosixFileAttributes readAttributes() throws java.io.IOException;
+ method public abstract void setGroup(java.nio.file.attribute.GroupPrincipal) throws java.io.IOException;
+ method public abstract void setPermissions(java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+ }
+
+ public abstract interface PosixFileAttributes implements java.nio.file.attribute.BasicFileAttributes {
+ method public abstract java.nio.file.attribute.GroupPrincipal group();
+ method public abstract java.nio.file.attribute.UserPrincipal owner();
+ method public abstract java.util.Set<java.nio.file.attribute.PosixFilePermission> permissions();
+ }
+
+ public final class PosixFilePermission extends java.lang.Enum {
+ method public static java.nio.file.attribute.PosixFilePermission valueOf(java.lang.String);
+ method public static final java.nio.file.attribute.PosixFilePermission[] values();
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_WRITE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_WRITE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_EXECUTE;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_READ;
+ enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_WRITE;
+ }
+
+ public final class PosixFilePermissions {
+ method public static java.nio.file.attribute.FileAttribute<java.util.Set<java.nio.file.attribute.PosixFilePermission>> asFileAttribute(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+ method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> fromString(java.lang.String);
+ method public static java.lang.String toString(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+ }
+
+ public abstract interface UserPrincipal implements java.security.Principal {
+ }
+
+ public abstract class UserPrincipalLookupService {
+ ctor protected UserPrincipalLookupService();
+ method public abstract java.nio.file.attribute.GroupPrincipal lookupPrincipalByGroupName(java.lang.String) throws java.io.IOException;
+ method public abstract java.nio.file.attribute.UserPrincipal lookupPrincipalByName(java.lang.String) throws java.io.IOException;
+ }
+
+ public class UserPrincipalNotFoundException extends java.io.IOException {
+ ctor public UserPrincipalNotFoundException(java.lang.String);
+ method public java.lang.String getName();
+ }
+
+}
+
+package java.nio.file.spi {
+
+ public abstract class FileSystemProvider {
+ ctor protected FileSystemProvider();
+ method public abstract void checkAccess(java.nio.file.Path, java.nio.file.AccessMode...) throws java.io.IOException;
+ method public abstract void copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public abstract void createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public void createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public void createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract void delete(java.nio.file.Path) throws java.io.IOException;
+ method public boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+ method public abstract V getFileAttributeView(java.nio.file.Path, java.lang.Class<V>, java.nio.file.LinkOption...);
+ method public abstract java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+ method public abstract java.nio.file.FileSystem getFileSystem(java.net.URI);
+ method public abstract java.nio.file.Path getPath(java.net.URI);
+ method public abstract java.lang.String getScheme();
+ method public static java.util.List<java.nio.file.spi.FileSystemProvider> installedProviders();
+ method public abstract boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+ method public abstract boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+ method public abstract void move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+ method public java.nio.channels.AsynchronousFileChannel newAsynchronousFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+ method public java.nio.channels.FileChannel newFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+ method public abstract java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public java.nio.file.FileSystem newFileSystem(java.nio.file.Path, java.util.Map<java.lang.String, ?>) throws java.io.IOException;
+ method public java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+ method public abstract A readAttributes(java.nio.file.Path, java.lang.Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public abstract java.util.Map<java.lang.String, java.lang.Object> readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption...) throws java.io.IOException;
+ method public java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+ method public abstract void setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption...) throws java.io.IOException;
+ }
+
+ public abstract class FileTypeDetector {
+ ctor protected FileTypeDetector();
+ method public abstract java.lang.String probeContentType(java.nio.file.Path) throws java.io.IOException;
+ }
+
+}
+
package java.security {
public final class AccessControlContext {
- ctor public AccessControlContext(java.security.AccessControlContext, java.security.DomainCombiner);
ctor public AccessControlContext(java.security.ProtectionDomain[]);
+ ctor public AccessControlContext(java.security.AccessControlContext, java.security.DomainCombiner);
method public void checkPermission(java.security.Permission) throws java.security.AccessControlException;
method public java.security.DomainCombiner getDomainCombiner();
}
@@ -50068,6 +51153,12 @@
method public static java.security.AccessControlContext getContext();
}
+ public abstract interface AlgorithmConstraints {
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.lang.String, java.security.AlgorithmParameters);
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.security.Key);
+ method public abstract boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.lang.String, java.security.Key, java.security.AlgorithmParameters);
+ }
+
public class AlgorithmParameterGenerator {
ctor protected AlgorithmParameterGenerator(java.security.AlgorithmParameterGeneratorSpi, java.security.Provider, java.lang.String);
method public final java.security.AlgorithmParameters generateParameters();
@@ -50117,9 +51208,11 @@
}
public final class AllPermission extends java.security.Permission {
- ctor public AllPermission(java.lang.String, java.lang.String);
ctor public AllPermission();
+ ctor public AllPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -50133,7 +51226,9 @@
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -50162,9 +51257,24 @@
method public boolean implies(java.security.CodeSource);
}
+ public final class CryptoPrimitive extends java.lang.Enum {
+ method public static java.security.CryptoPrimitive valueOf(java.lang.String);
+ method public static final java.security.CryptoPrimitive[] values();
+ enum_constant public static final java.security.CryptoPrimitive BLOCK_CIPHER;
+ enum_constant public static final java.security.CryptoPrimitive KEY_AGREEMENT;
+ enum_constant public static final java.security.CryptoPrimitive KEY_ENCAPSULATION;
+ enum_constant public static final java.security.CryptoPrimitive KEY_WRAP;
+ enum_constant public static final java.security.CryptoPrimitive MAC;
+ enum_constant public static final java.security.CryptoPrimitive MESSAGE_DIGEST;
+ enum_constant public static final java.security.CryptoPrimitive PUBLIC_KEY_ENCRYPTION;
+ enum_constant public static final java.security.CryptoPrimitive SECURE_RANDOM;
+ enum_constant public static final java.security.CryptoPrimitive SIGNATURE;
+ enum_constant public static final java.security.CryptoPrimitive STREAM_CIPHER;
+ }
+
public class DigestException extends java.security.GeneralSecurityException {
- ctor public DigestException(java.lang.String);
ctor public DigestException();
+ ctor public DigestException(java.lang.String);
ctor public DigestException(java.lang.String, java.lang.Throwable);
ctor public DigestException(java.lang.Throwable);
}
@@ -50190,8 +51300,8 @@
}
public class GeneralSecurityException extends java.lang.Exception {
- ctor public GeneralSecurityException(java.lang.String);
ctor public GeneralSecurityException();
+ ctor public GeneralSecurityException(java.lang.String);
ctor public GeneralSecurityException(java.lang.String, java.lang.Throwable);
ctor public GeneralSecurityException(java.lang.Throwable);
}
@@ -50207,8 +51317,8 @@
public abstract deprecated class Identity implements java.security.Principal java.io.Serializable {
ctor protected Identity();
- ctor public Identity(java.lang.String);
ctor public Identity(java.lang.String, java.security.IdentityScope) throws java.security.KeyManagementException;
+ ctor public Identity(java.lang.String);
method public void addCertificate(java.security.Certificate) throws java.security.KeyManagementException;
method public java.security.Certificate[] certificates();
method public final boolean equals(java.lang.Object);
@@ -50239,22 +51349,22 @@
}
public class InvalidAlgorithmParameterException extends java.security.GeneralSecurityException {
- ctor public InvalidAlgorithmParameterException(java.lang.String);
ctor public InvalidAlgorithmParameterException();
+ ctor public InvalidAlgorithmParameterException(java.lang.String);
ctor public InvalidAlgorithmParameterException(java.lang.String, java.lang.Throwable);
ctor public InvalidAlgorithmParameterException(java.lang.Throwable);
}
public class InvalidKeyException extends java.security.KeyException {
- ctor public InvalidKeyException(java.lang.String);
ctor public InvalidKeyException();
+ ctor public InvalidKeyException(java.lang.String);
ctor public InvalidKeyException(java.lang.String, java.lang.Throwable);
ctor public InvalidKeyException(java.lang.Throwable);
}
public class InvalidParameterException extends java.lang.IllegalArgumentException {
- ctor public InvalidParameterException(java.lang.String);
ctor public InvalidParameterException();
+ ctor public InvalidParameterException(java.lang.String);
}
public abstract interface Key implements java.io.Serializable {
@@ -50265,8 +51375,8 @@
}
public class KeyException extends java.security.GeneralSecurityException {
- ctor public KeyException(java.lang.String);
ctor public KeyException();
+ ctor public KeyException(java.lang.String);
ctor public KeyException(java.lang.String, java.lang.Throwable);
ctor public KeyException(java.lang.Throwable);
}
@@ -50293,8 +51403,8 @@
}
public class KeyManagementException extends java.security.KeyException {
- ctor public KeyManagementException(java.lang.String);
ctor public KeyManagementException();
+ ctor public KeyManagementException(java.lang.String);
ctor public KeyManagementException(java.lang.String, java.lang.Throwable);
ctor public KeyManagementException(java.lang.Throwable);
}
@@ -50315,8 +51425,8 @@
method public static java.security.KeyPairGenerator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
method public void initialize(int);
- method public void initialize(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
method public void initialize(int, java.security.SecureRandom);
+ method public void initialize(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
}
public abstract class KeyPairGeneratorSpi {
@@ -50419,8 +51529,8 @@
}
public class KeyStoreException extends java.security.GeneralSecurityException {
- ctor public KeyStoreException(java.lang.String);
ctor public KeyStoreException();
+ ctor public KeyStoreException(java.lang.String);
ctor public KeyStoreException(java.lang.String, java.lang.Throwable);
ctor public KeyStoreException(java.lang.Throwable);
}
@@ -50482,22 +51592,24 @@
}
public class NoSuchAlgorithmException extends java.security.GeneralSecurityException {
- ctor public NoSuchAlgorithmException(java.lang.String);
ctor public NoSuchAlgorithmException();
+ ctor public NoSuchAlgorithmException(java.lang.String);
ctor public NoSuchAlgorithmException(java.lang.String, java.lang.Throwable);
ctor public NoSuchAlgorithmException(java.lang.Throwable);
}
public class NoSuchProviderException extends java.security.GeneralSecurityException {
- ctor public NoSuchProviderException(java.lang.String);
ctor public NoSuchProviderException();
+ ctor public NoSuchProviderException(java.lang.String);
}
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
+ method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
+ method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -50605,8 +51717,8 @@
}
public class ProviderException extends java.lang.RuntimeException {
- ctor public ProviderException(java.lang.String);
ctor public ProviderException();
+ ctor public ProviderException(java.lang.String);
ctor public ProviderException(java.lang.String, java.lang.Throwable);
ctor public ProviderException(java.lang.Throwable);
}
@@ -50616,8 +51728,8 @@
}
public class SecureClassLoader extends java.lang.ClassLoader {
- ctor protected SecureClassLoader();
ctor protected SecureClassLoader(java.lang.ClassLoader);
+ ctor protected SecureClassLoader();
method protected final java.lang.Class<?> defineClass(java.lang.String, byte[], int, int, java.security.CodeSource);
method protected final java.lang.Class<?> defineClass(java.lang.String, java.nio.ByteBuffer, java.security.CodeSource);
method protected java.security.PermissionCollection getPermissions(java.security.CodeSource);
@@ -50650,10 +51762,10 @@
method public static deprecated java.lang.String getAlgorithmProperty(java.lang.String, java.lang.String);
method public static java.util.Set<java.lang.String> getAlgorithms(java.lang.String);
method public static java.lang.String getProperty(java.lang.String);
- method public static synchronized java.security.Provider getProvider(java.lang.String);
- method public static synchronized java.security.Provider[] getProviders();
+ method public static java.security.Provider getProvider(java.lang.String);
+ method public static java.security.Provider[] getProviders();
method public static java.security.Provider[] getProviders(java.lang.String);
- method public static synchronized java.security.Provider[] getProviders(java.util.Map<java.lang.String, java.lang.String>);
+ method public static java.security.Provider[] getProviders(java.util.Map<java.lang.String, java.lang.String>);
method public static synchronized int insertProviderAt(java.security.Provider, int);
method public static synchronized void removeProvider(java.lang.String);
method public static void setProperty(java.lang.String, java.lang.String);
@@ -50667,6 +51779,7 @@
public abstract class Signature extends java.security.SignatureSpi {
ctor protected Signature(java.lang.String);
method public final java.lang.String getAlgorithm();
+ method public java.security.SignatureSpi getCurrentSpi();
method public static java.security.Signature getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
method public static java.security.Signature getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.Signature getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
@@ -50694,8 +51807,8 @@
}
public class SignatureException extends java.security.GeneralSecurityException {
- ctor public SignatureException(java.lang.String);
ctor public SignatureException();
+ ctor public SignatureException(java.lang.String);
ctor public SignatureException(java.lang.String, java.lang.Throwable);
ctor public SignatureException(java.lang.Throwable);
}
@@ -50748,17 +51861,19 @@
}
public class UnrecoverableKeyException extends java.security.UnrecoverableEntryException {
- ctor public UnrecoverableKeyException(java.lang.String);
ctor public UnrecoverableKeyException();
+ ctor public UnrecoverableKeyException(java.lang.String);
}
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -50832,12 +51947,28 @@
}
public class CRLException extends java.security.GeneralSecurityException {
- ctor public CRLException(java.lang.String);
ctor public CRLException();
+ ctor public CRLException(java.lang.String);
ctor public CRLException(java.lang.String, java.lang.Throwable);
ctor public CRLException(java.lang.Throwable);
}
+ public final class CRLReason extends java.lang.Enum {
+ method public static java.security.cert.CRLReason valueOf(java.lang.String);
+ method public static final java.security.cert.CRLReason[] values();
+ enum_constant public static final java.security.cert.CRLReason AA_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason AFFILIATION_CHANGED;
+ enum_constant public static final java.security.cert.CRLReason CA_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason CERTIFICATE_HOLD;
+ enum_constant public static final java.security.cert.CRLReason CESSATION_OF_OPERATION;
+ enum_constant public static final java.security.cert.CRLReason KEY_COMPROMISE;
+ enum_constant public static final java.security.cert.CRLReason PRIVILEGE_WITHDRAWN;
+ enum_constant public static final java.security.cert.CRLReason REMOVE_FROM_CRL;
+ enum_constant public static final java.security.cert.CRLReason SUPERSEDED;
+ enum_constant public static final java.security.cert.CRLReason UNSPECIFIED;
+ enum_constant public static final java.security.cert.CRLReason UNUSED;
+ }
+
public abstract interface CRLSelector implements java.lang.Cloneable {
method public abstract java.lang.Object clone();
method public abstract boolean match(java.security.cert.CRL);
@@ -50870,10 +52001,10 @@
}
public class CertPathBuilderException extends java.security.GeneralSecurityException {
- ctor public CertPathBuilderException(java.lang.String, java.lang.Throwable);
- ctor public CertPathBuilderException(java.lang.Throwable);
- ctor public CertPathBuilderException(java.lang.String);
ctor public CertPathBuilderException();
+ ctor public CertPathBuilderException(java.lang.String);
+ ctor public CertPathBuilderException(java.lang.Throwable);
+ ctor public CertPathBuilderException(java.lang.String, java.lang.Throwable);
}
public abstract interface CertPathBuilderResult implements java.lang.Cloneable {
@@ -50902,13 +52033,30 @@
}
public class CertPathValidatorException extends java.security.GeneralSecurityException {
- ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int);
- ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable);
- ctor public CertPathValidatorException(java.lang.Throwable);
- ctor public CertPathValidatorException(java.lang.String);
ctor public CertPathValidatorException();
+ ctor public CertPathValidatorException(java.lang.String);
+ ctor public CertPathValidatorException(java.lang.Throwable);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int);
+ ctor public CertPathValidatorException(java.lang.String, java.lang.Throwable, java.security.cert.CertPath, int, java.security.cert.CertPathValidatorException.Reason);
method public java.security.cert.CertPath getCertPath();
method public int getIndex();
+ method public java.security.cert.CertPathValidatorException.Reason getReason();
+ }
+
+ public static final class CertPathValidatorException.BasicReason extends java.lang.Enum implements java.security.cert.CertPathValidatorException.Reason {
+ method public static java.security.cert.CertPathValidatorException.BasicReason valueOf(java.lang.String);
+ method public static final java.security.cert.CertPathValidatorException.BasicReason[] values();
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason ALGORITHM_CONSTRAINED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason EXPIRED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason INVALID_SIGNATURE;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason NOT_YET_VALID;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason REVOKED;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNDETERMINED_REVOCATION_STATUS;
+ enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNSPECIFIED;
+ }
+
+ public static abstract interface CertPathValidatorException.Reason implements java.io.Serializable {
}
public abstract interface CertPathValidatorResult implements java.lang.Cloneable {
@@ -50939,10 +52087,10 @@
}
public class CertStoreException extends java.security.GeneralSecurityException {
- ctor public CertStoreException(java.lang.String, java.lang.Throwable);
- ctor public CertStoreException(java.lang.Throwable);
- ctor public CertStoreException(java.lang.String);
ctor public CertStoreException();
+ ctor public CertStoreException(java.lang.String);
+ ctor public CertStoreException(java.lang.Throwable);
+ ctor public CertStoreException(java.lang.String, java.lang.Throwable);
}
public abstract interface CertStoreParameters implements java.lang.Cloneable {
@@ -50972,22 +52120,22 @@
}
public class CertificateEncodingException extends java.security.cert.CertificateException {
- ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException();
+ ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException(java.lang.String, java.lang.Throwable);
ctor public CertificateEncodingException(java.lang.Throwable);
}
public class CertificateException extends java.security.GeneralSecurityException {
- ctor public CertificateException(java.lang.String);
ctor public CertificateException();
+ ctor public CertificateException(java.lang.String);
ctor public CertificateException(java.lang.String, java.lang.Throwable);
ctor public CertificateException(java.lang.Throwable);
}
public class CertificateExpiredException extends java.security.cert.CertificateException {
- ctor public CertificateExpiredException(java.lang.String);
ctor public CertificateExpiredException();
+ ctor public CertificateExpiredException(java.lang.String);
}
public class CertificateFactory {
@@ -51020,28 +52168,44 @@
}
public class CertificateNotYetValidException extends java.security.cert.CertificateException {
- ctor public CertificateNotYetValidException(java.lang.String);
ctor public CertificateNotYetValidException();
+ ctor public CertificateNotYetValidException(java.lang.String);
}
public class CertificateParsingException extends java.security.cert.CertificateException {
- ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException();
+ ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException(java.lang.String, java.lang.Throwable);
ctor public CertificateParsingException(java.lang.Throwable);
}
+ public class CertificateRevokedException extends java.security.cert.CertificateException {
+ ctor public CertificateRevokedException(java.util.Date, java.security.cert.CRLReason, javax.security.auth.x500.X500Principal, java.util.Map<java.lang.String, java.security.cert.Extension>);
+ method public javax.security.auth.x500.X500Principal getAuthorityName();
+ method public java.util.Map<java.lang.String, java.security.cert.Extension> getExtensions();
+ method public java.util.Date getInvalidityDate();
+ method public java.util.Date getRevocationDate();
+ method public java.security.cert.CRLReason getRevocationReason();
+ }
+
public class CollectionCertStoreParameters implements java.security.cert.CertStoreParameters {
- ctor public CollectionCertStoreParameters();
ctor public CollectionCertStoreParameters(java.util.Collection<?>);
+ ctor public CollectionCertStoreParameters();
method public java.lang.Object clone();
method public java.util.Collection<?> getCollection();
}
+ public abstract interface Extension {
+ method public abstract void encode(java.io.OutputStream) throws java.io.IOException;
+ method public abstract java.lang.String getId();
+ method public abstract byte[] getValue();
+ method public abstract boolean isCritical();
+ }
+
public class LDAPCertStoreParameters implements java.security.cert.CertStoreParameters {
ctor public LDAPCertStoreParameters(java.lang.String, int);
- ctor public LDAPCertStoreParameters();
ctor public LDAPCertStoreParameters(java.lang.String);
+ ctor public LDAPCertStoreParameters();
method public java.lang.Object clone();
method public int getPort();
method public java.lang.String getServerName();
@@ -51108,6 +52272,19 @@
method public void setTrustAnchors(java.util.Set<java.security.cert.TrustAnchor>) throws java.security.InvalidAlgorithmParameterException;
}
+ public final class PKIXReason extends java.lang.Enum implements java.security.cert.CertPathValidatorException.Reason {
+ method public static java.security.cert.PKIXReason valueOf(java.lang.String);
+ method public static final java.security.cert.PKIXReason[] values();
+ enum_constant public static final java.security.cert.PKIXReason INVALID_KEY_USAGE;
+ enum_constant public static final java.security.cert.PKIXReason INVALID_NAME;
+ enum_constant public static final java.security.cert.PKIXReason INVALID_POLICY;
+ enum_constant public static final java.security.cert.PKIXReason NAME_CHAINING;
+ enum_constant public static final java.security.cert.PKIXReason NOT_CA_CERT;
+ enum_constant public static final java.security.cert.PKIXReason NO_TRUST_ANCHOR;
+ enum_constant public static final java.security.cert.PKIXReason PATH_TOO_LONG;
+ enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
+ }
+
public abstract interface PolicyNode {
method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
method public abstract int getDepth();
@@ -51127,8 +52304,8 @@
public class TrustAnchor {
ctor public TrustAnchor(java.security.cert.X509Certificate, byte[]);
- ctor public TrustAnchor(java.lang.String, java.security.PublicKey, byte[]);
ctor public TrustAnchor(javax.security.auth.x500.X500Principal, java.security.PublicKey, byte[]);
+ ctor public TrustAnchor(java.lang.String, java.security.PublicKey, byte[]);
method public final javax.security.auth.x500.X500Principal getCA();
method public final java.lang.String getCAName();
method public final java.security.PublicKey getCAPublicKey();
@@ -51161,6 +52338,7 @@
method public javax.security.auth.x500.X500Principal getCertificateIssuer();
method public abstract byte[] getEncoded() throws java.security.cert.CRLException;
method public abstract java.util.Date getRevocationDate();
+ method public java.security.cert.CRLReason getRevocationReason();
method public abstract java.math.BigInteger getSerialNumber();
method public abstract boolean hasExtensions();
method public abstract java.lang.String toString();
@@ -51436,8 +52614,8 @@
}
public class EllipticCurve {
- ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger, byte[]);
ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger);
+ ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger, byte[]);
method public java.math.BigInteger getA();
method public java.math.BigInteger getB();
method public java.security.spec.ECField getField();
@@ -51451,15 +52629,15 @@
}
public class InvalidKeySpecException extends java.security.GeneralSecurityException {
- ctor public InvalidKeySpecException(java.lang.String);
ctor public InvalidKeySpecException();
+ ctor public InvalidKeySpecException(java.lang.String);
ctor public InvalidKeySpecException(java.lang.String, java.lang.Throwable);
ctor public InvalidKeySpecException(java.lang.Throwable);
}
public class InvalidParameterSpecException extends java.security.GeneralSecurityException {
- ctor public InvalidParameterSpecException(java.lang.String);
ctor public InvalidParameterSpecException();
+ ctor public InvalidParameterSpecException(java.lang.String);
}
public abstract interface KeySpec {
@@ -51480,8 +52658,8 @@
}
public class PSSParameterSpec implements java.security.spec.AlgorithmParameterSpec {
- ctor public PSSParameterSpec(int);
ctor public PSSParameterSpec(java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec, int, int);
+ ctor public PSSParameterSpec(int);
method public java.lang.String getDigestAlgorithm();
method public java.lang.String getMGFAlgorithm();
method public java.security.spec.AlgorithmParameterSpec getMGFParameters();
@@ -51550,28 +52728,28 @@
public abstract interface Array {
method public abstract void free() throws java.sql.SQLException;
method public abstract java.lang.Object getArray() throws java.sql.SQLException;
+ method public abstract java.lang.Object getArray(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getArray(long, int) throws java.sql.SQLException;
method public abstract java.lang.Object getArray(long, int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
- method public abstract java.lang.Object getArray(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract int getBaseType() throws java.sql.SQLException;
method public abstract java.lang.String getBaseTypeName() throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet() throws java.sql.SQLException;
+ method public abstract java.sql.ResultSet getResultSet(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet(long, int) throws java.sql.SQLException;
method public abstract java.sql.ResultSet getResultSet(long, int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
- method public abstract java.sql.ResultSet getResultSet(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
}
- public class BatchUpdateException extends java.sql.SQLException implements java.io.Serializable {
+ public class BatchUpdateException extends java.sql.SQLException {
+ ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[]);
+ ctor public BatchUpdateException(java.lang.String, java.lang.String, int[]);
+ ctor public BatchUpdateException(java.lang.String, int[]);
+ ctor public BatchUpdateException(int[]);
ctor public BatchUpdateException();
ctor public BatchUpdateException(java.lang.Throwable);
ctor public BatchUpdateException(int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, java.lang.String, int[], java.lang.Throwable);
ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[], java.lang.Throwable);
- ctor public BatchUpdateException(int[]);
- ctor public BatchUpdateException(java.lang.String, int[]);
- ctor public BatchUpdateException(java.lang.String, java.lang.String, int[]);
- ctor public BatchUpdateException(java.lang.String, java.lang.String, int, int[]);
method public int[] getUpdateCounts();
}
@@ -51581,8 +52759,8 @@
method public abstract java.io.InputStream getBinaryStream(long, long) throws java.sql.SQLException;
method public abstract byte[] getBytes(long, int) throws java.sql.SQLException;
method public abstract long length() throws java.sql.SQLException;
- method public abstract long position(java.sql.Blob, long) throws java.sql.SQLException;
method public abstract long position(byte[], long) throws java.sql.SQLException;
+ method public abstract long position(java.sql.Blob, long) throws java.sql.SQLException;
method public abstract java.io.OutputStream setBinaryStream(long) throws java.sql.SQLException;
method public abstract int setBytes(long, byte[]) throws java.sql.SQLException;
method public abstract int setBytes(long, byte[], int, int) throws java.sql.SQLException;
@@ -51592,8 +52770,8 @@
public abstract interface CallableStatement implements java.sql.PreparedStatement {
method public abstract java.sql.Array getArray(int) throws java.sql.SQLException;
method public abstract java.sql.Array getArray(java.lang.String) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(int) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(java.lang.String) throws java.sql.SQLException;
@@ -51688,9 +52866,9 @@
method public abstract void setNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(java.lang.String, java.lang.Object, int, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
method public abstract void setRowId(java.lang.String, java.sql.RowId) throws java.sql.SQLException;
method public abstract void setSQLXML(java.lang.String, java.sql.SQLXML) throws java.sql.SQLException;
method public abstract void setShort(java.lang.String, short) throws java.sql.SQLException;
@@ -51719,8 +52897,8 @@
method public abstract java.io.Reader getCharacterStream(long, long) throws java.sql.SQLException;
method public abstract java.lang.String getSubString(long, int) throws java.sql.SQLException;
method public abstract long length() throws java.sql.SQLException;
- method public abstract long position(java.sql.Clob, long) throws java.sql.SQLException;
method public abstract long position(java.lang.String, long) throws java.sql.SQLException;
+ method public abstract long position(java.sql.Clob, long) throws java.sql.SQLException;
method public abstract java.io.OutputStream setAsciiStream(long) throws java.sql.SQLException;
method public abstract java.io.Writer setCharacterStream(long) throws java.sql.SQLException;
method public abstract int setString(long, java.lang.String) throws java.sql.SQLException;
@@ -51758,10 +52936,10 @@
method public abstract java.sql.CallableStatement prepareCall(java.lang.String, int, int) throws java.sql.SQLException;
method public abstract java.sql.CallableStatement prepareCall(java.lang.String, int, int, int) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String) throws java.sql.SQLException;
- method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int) throws java.sql.SQLException;
- method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int[]) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int, int) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int, int, int) throws java.sql.SQLException;
+ method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, int[]) throws java.sql.SQLException;
method public abstract java.sql.PreparedStatement prepareStatement(java.lang.String, java.lang.String[]) throws java.sql.SQLException;
method public abstract void releaseSavepoint(java.sql.Savepoint) throws java.sql.SQLException;
method public abstract void rollback() throws java.sql.SQLException;
@@ -51783,7 +52961,7 @@
field public static final int TRANSACTION_SERIALIZABLE = 8; // 0x8
}
- public class DataTruncation extends java.sql.SQLWarning implements java.io.Serializable {
+ public class DataTruncation extends java.sql.SQLWarning {
ctor public DataTruncation(int, boolean, boolean, int, int);
ctor public DataTruncation(int, boolean, boolean, int, int, java.lang.Throwable);
method public int getDataSize();
@@ -52045,17 +53223,17 @@
}
public class DriverManager {
- method public static void deregisterDriver(java.sql.Driver) throws java.sql.SQLException;
- method public static java.sql.Connection getConnection(java.lang.String) throws java.sql.SQLException;
+ method public static synchronized void deregisterDriver(java.sql.Driver) throws java.sql.SQLException;
method public static java.sql.Connection getConnection(java.lang.String, java.util.Properties) throws java.sql.SQLException;
method public static java.sql.Connection getConnection(java.lang.String, java.lang.String, java.lang.String) throws java.sql.SQLException;
+ method public static java.sql.Connection getConnection(java.lang.String) throws java.sql.SQLException;
method public static java.sql.Driver getDriver(java.lang.String) throws java.sql.SQLException;
method public static java.util.Enumeration<java.sql.Driver> getDrivers();
method public static deprecated java.io.PrintStream getLogStream();
method public static java.io.PrintWriter getLogWriter();
method public static int getLoginTimeout();
method public static void println(java.lang.String);
- method public static void registerDriver(java.sql.Driver) throws java.sql.SQLException;
+ method public static synchronized void registerDriver(java.sql.Driver) throws java.sql.SQLException;
method public static deprecated void setLogStream(java.io.PrintStream);
method public static void setLogWriter(java.io.PrintWriter);
method public static void setLoginTimeout(int);
@@ -52134,8 +53312,8 @@
method public abstract void setNString(int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(int, int) throws java.sql.SQLException;
method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int, int) throws java.sql.SQLException;
method public abstract void setRef(int, java.sql.Ref) throws java.sql.SQLException;
method public abstract void setRowId(int, java.sql.RowId) throws java.sql.SQLException;
@@ -52152,8 +53330,8 @@
public abstract interface Ref {
method public abstract java.lang.String getBaseTypeName() throws java.sql.SQLException;
- method public abstract java.lang.Object getObject() throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
+ method public abstract java.lang.Object getObject() throws java.sql.SQLException;
method public abstract void setObject(java.lang.Object) throws java.sql.SQLException;
}
@@ -52171,10 +53349,10 @@
method public abstract java.sql.Array getArray(java.lang.String) throws java.sql.SQLException;
method public abstract java.io.InputStream getAsciiStream(int) throws java.sql.SQLException;
method public abstract java.io.InputStream getAsciiStream(java.lang.String) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
- method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract deprecated java.math.BigDecimal getBigDecimal(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
+ method public abstract java.math.BigDecimal getBigDecimal(java.lang.String) throws java.sql.SQLException;
method public abstract java.io.InputStream getBinaryStream(int) throws java.sql.SQLException;
method public abstract java.io.InputStream getBinaryStream(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Blob getBlob(int) throws java.sql.SQLException;
@@ -52192,8 +53370,8 @@
method public abstract int getConcurrency() throws java.sql.SQLException;
method public abstract java.lang.String getCursorName() throws java.sql.SQLException;
method public abstract java.sql.Date getDate(int) throws java.sql.SQLException;
- method public abstract java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Date getDate(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Date getDate(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract double getDouble(int) throws java.sql.SQLException;
method public abstract double getDouble(java.lang.String) throws java.sql.SQLException;
@@ -52214,8 +53392,8 @@
method public abstract java.lang.String getNString(int) throws java.sql.SQLException;
method public abstract java.lang.String getNString(java.lang.String) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(int) throws java.sql.SQLException;
- method public abstract java.lang.Object getObject(int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.lang.Object getObject(int, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.lang.Object getObject(java.lang.String, java.util.Map<java.lang.String, java.lang.Class<?>>) throws java.sql.SQLException;
method public abstract java.sql.Ref getRef(int) throws java.sql.SQLException;
method public abstract java.sql.Ref getRef(java.lang.String) throws java.sql.SQLException;
@@ -52230,12 +53408,12 @@
method public abstract java.lang.String getString(int) throws java.sql.SQLException;
method public abstract java.lang.String getString(java.lang.String) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(int) throws java.sql.SQLException;
- method public abstract java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Time getTime(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(int) throws java.sql.SQLException;
- method public abstract java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(java.lang.String) throws java.sql.SQLException;
+ method public abstract java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
method public abstract java.sql.Timestamp getTimestamp(java.lang.String, java.util.Calendar) throws java.sql.SQLException;
method public abstract int getType() throws java.sql.SQLException;
method public abstract java.net.URL getURL(int) throws java.sql.SQLException;
@@ -52325,10 +53503,10 @@
method public abstract void updateNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void updateNull(int) throws java.sql.SQLException;
method public abstract void updateNull(java.lang.String) throws java.sql.SQLException;
- method public abstract void updateObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateObject(int, java.lang.Object, int) throws java.sql.SQLException;
- method public abstract void updateObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
+ method public abstract void updateObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void updateObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
method public abstract void updateRef(int, java.sql.Ref) throws java.sql.SQLException;
method public abstract void updateRef(java.lang.String, java.sql.Ref) throws java.sql.SQLException;
method public abstract void updateRow() throws java.sql.SQLException;
@@ -52407,10 +53585,10 @@
ctor public SQLClientInfoException(java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
ctor public SQLClientInfoException(java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
ctor public SQLClientInfoException(java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
- ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
- ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
ctor public SQLClientInfoException(java.lang.String, java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
ctor public SQLClientInfoException(java.lang.String, java.lang.String, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
+ ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>);
+ ctor public SQLClientInfoException(java.lang.String, java.lang.String, int, java.util.Map<java.lang.String, java.sql.ClientInfoStatus>, java.lang.Throwable);
method public java.util.Map<java.lang.String, java.sql.ClientInfoStatus> getFailedProperties();
}
@@ -52431,11 +53609,11 @@
ctor public SQLDataException(java.lang.String, java.lang.String, int, java.lang.Throwable);
}
- public class SQLException extends java.lang.Exception implements java.lang.Iterable java.io.Serializable {
- ctor public SQLException();
- ctor public SQLException(java.lang.String);
- ctor public SQLException(java.lang.String, java.lang.String);
+ public class SQLException extends java.lang.Exception implements java.lang.Iterable {
ctor public SQLException(java.lang.String, java.lang.String, int);
+ ctor public SQLException(java.lang.String, java.lang.String);
+ ctor public SQLException(java.lang.String);
+ ctor public SQLException();
ctor public SQLException(java.lang.Throwable);
ctor public SQLException(java.lang.String, java.lang.Throwable);
ctor public SQLException(java.lang.String, java.lang.String, java.lang.Throwable);
@@ -52562,7 +53740,7 @@
method public abstract void writeURL(java.net.URL) throws java.sql.SQLException;
}
- public final class SQLPermission extends java.security.BasicPermission implements java.security.Guard java.io.Serializable {
+ public final class SQLPermission extends java.security.BasicPermission {
ctor public SQLPermission(java.lang.String);
ctor public SQLPermission(java.lang.String, java.lang.String);
}
@@ -52633,11 +53811,11 @@
ctor public SQLTransientException(java.lang.String, java.lang.String, int, java.lang.Throwable);
}
- public class SQLWarning extends java.sql.SQLException implements java.io.Serializable {
- ctor public SQLWarning();
- ctor public SQLWarning(java.lang.String);
- ctor public SQLWarning(java.lang.String, java.lang.String);
+ public class SQLWarning extends java.sql.SQLException {
ctor public SQLWarning(java.lang.String, java.lang.String, int);
+ ctor public SQLWarning(java.lang.String, java.lang.String);
+ ctor public SQLWarning(java.lang.String);
+ ctor public SQLWarning();
ctor public SQLWarning(java.lang.Throwable);
ctor public SQLWarning(java.lang.String, java.lang.Throwable);
ctor public SQLWarning(java.lang.String, java.lang.String, java.lang.Throwable);
@@ -52726,15 +53904,15 @@
}
public class Timestamp extends java.util.Date {
- ctor public deprecated Timestamp(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ ctor public deprecated Timestamp(int, int, int, int, int, int, int);
ctor public Timestamp(long);
method public boolean after(java.sql.Timestamp);
method public boolean before(java.sql.Timestamp);
method public int compareTo(java.sql.Timestamp);
method public boolean equals(java.sql.Timestamp);
method public int getNanos();
- method public void setNanos(int) throws java.lang.IllegalArgumentException;
- method public static java.sql.Timestamp valueOf(java.lang.String) throws java.lang.IllegalArgumentException;
+ method public void setNanos(int);
+ method public static java.sql.Timestamp valueOf(java.lang.String);
}
public class Types {
@@ -52814,11 +53992,11 @@
}
public class AttributedString {
+ ctor public AttributedString(java.lang.String);
+ ctor public AttributedString(java.lang.String, java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>);
ctor public AttributedString(java.text.AttributedCharacterIterator);
ctor public AttributedString(java.text.AttributedCharacterIterator, int, int);
ctor public AttributedString(java.text.AttributedCharacterIterator, int, int, java.text.AttributedCharacterIterator.Attribute[]);
- ctor public AttributedString(java.lang.String);
- ctor public AttributedString(java.lang.String, java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>);
method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, java.lang.Object);
method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, java.lang.Object, int, int);
method public void addAttributes(java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute, ?>, int, int);
@@ -52828,9 +54006,9 @@
}
public final class Bidi {
+ ctor public Bidi(java.lang.String, int);
ctor public Bidi(java.text.AttributedCharacterIterator);
ctor public Bidi(char[], int, byte[], int, int, int);
- ctor public Bidi(java.lang.String, int);
method public boolean baseIsLeftToRight();
method public java.text.Bidi createLineBidi(int, int);
method public int getBaseLevel();
@@ -52857,7 +54035,7 @@
method public abstract int current();
method public abstract int first();
method public abstract int following(int);
- method public static java.util.Locale[] getAvailableLocales();
+ method public static synchronized java.util.Locale[] getAvailableLocales();
method public static java.text.BreakIterator getCharacterInstance();
method public static java.text.BreakIterator getCharacterInstance(java.util.Locale);
method public static java.text.BreakIterator getLineInstance();
@@ -52869,8 +54047,8 @@
method public static java.text.BreakIterator getWordInstance(java.util.Locale);
method public boolean isBoundary(int);
method public abstract int last();
- method public abstract int next();
method public abstract int next(int);
+ method public abstract int next();
method public int preceding(int);
method public abstract int previous();
method public void setText(java.lang.String);
@@ -52893,11 +54071,11 @@
}
public class ChoiceFormat extends java.text.NumberFormat {
- ctor public ChoiceFormat(double[], java.lang.String[]);
ctor public ChoiceFormat(java.lang.String);
+ ctor public ChoiceFormat(double[], java.lang.String[]);
method public void applyPattern(java.lang.String);
- method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
+ method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.Object[] getFormats();
method public double[] getLimits();
method public static final double nextDouble(double);
@@ -52917,8 +54095,8 @@
method public void reset();
method public static final short secondaryOrder(int);
method public void setOffset(int);
- method public void setText(java.text.CharacterIterator);
method public void setText(java.lang.String);
+ method public void setText(java.text.CharacterIterator);
method public static final short tertiaryOrder(int);
field public static final int NULLORDER = -1; // 0xffffffff
}
@@ -52933,18 +54111,18 @@
public abstract class Collator implements java.lang.Cloneable java.util.Comparator {
ctor protected Collator();
method public java.lang.Object clone();
- method public int compare(java.lang.Object, java.lang.Object);
method public abstract int compare(java.lang.String, java.lang.String);
+ method public int compare(java.lang.Object, java.lang.Object);
method public boolean equals(java.lang.String, java.lang.String);
- method public static java.util.Locale[] getAvailableLocales();
+ method public static synchronized java.util.Locale[] getAvailableLocales();
method public abstract java.text.CollationKey getCollationKey(java.lang.String);
- method public int getDecomposition();
- method public static java.text.Collator getInstance();
- method public static java.text.Collator getInstance(java.util.Locale);
- method public int getStrength();
+ method public synchronized int getDecomposition();
+ method public static synchronized java.text.Collator getInstance();
+ method public static synchronized java.text.Collator getInstance(java.util.Locale);
+ method public synchronized int getStrength();
method public abstract int hashCode();
- method public void setDecomposition(int);
- method public void setStrength(int);
+ method public synchronized void setDecomposition(int);
+ method public synchronized void setStrength(int);
field public static final int CANONICAL_DECOMPOSITION = 1; // 0x1
field public static final int FULL_DECOMPOSITION = 2; // 0x2
field public static final int IDENTICAL = 3; // 0x3
@@ -52957,8 +54135,8 @@
public abstract class DateFormat extends java.text.Format {
ctor protected DateFormat();
method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.String format(java.util.Date);
method public abstract java.lang.StringBuffer format(java.util.Date, java.lang.StringBuffer, java.text.FieldPosition);
+ method public final java.lang.String format(java.util.Date);
method public static java.util.Locale[] getAvailableLocales();
method public java.util.Calendar getCalendar();
method public static final java.text.DateFormat getDateInstance();
@@ -53063,9 +54241,9 @@
ctor public DecimalFormat(java.lang.String, java.text.DecimalFormatSymbols);
method public void applyLocalizedPattern(java.lang.String);
method public void applyPattern(java.lang.String);
+ method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.DecimalFormatSymbols getDecimalFormatSymbols();
method public int getGroupingSize();
method public int getMultiplier();
@@ -53101,15 +54279,17 @@
method public java.lang.String getExponentSeparator();
method public char getGroupingSeparator();
method public java.lang.String getInfinity();
- method public static java.text.DecimalFormatSymbols getInstance();
- method public static java.text.DecimalFormatSymbols getInstance(java.util.Locale);
+ method public static final java.text.DecimalFormatSymbols getInstance();
+ method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
method public java.lang.String getInternationalCurrencySymbol();
method public char getMinusSign();
+ method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
method public java.lang.String getNaN();
method public char getPatternSeparator();
method public char getPerMill();
method public char getPercent();
+ method public java.lang.String getPercentString();
method public char getZeroDigit();
method public void setCurrency(java.util.Currency);
method public void setCurrencySymbol(java.lang.String);
@@ -53146,8 +54326,8 @@
method public final java.lang.String format(java.lang.Object);
method public abstract java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.AttributedCharacterIterator formatToCharacterIterator(java.lang.Object);
- method public java.lang.Object parseObject(java.lang.String) throws java.text.ParseException;
method public abstract java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Object parseObject(java.lang.String) throws java.text.ParseException;
}
public static class Format.Field extends java.text.AttributedCharacterIterator.Attribute {
@@ -53155,17 +54335,17 @@
}
public class MessageFormat extends java.text.Format {
- ctor public MessageFormat(java.lang.String, java.util.Locale);
ctor public MessageFormat(java.lang.String);
+ ctor public MessageFormat(java.lang.String, java.util.Locale);
method public void applyPattern(java.lang.String);
method public final java.lang.StringBuffer format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public static java.lang.String format(java.lang.String, java.lang.Object...);
+ method public final java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
method public java.text.Format[] getFormats();
method public java.text.Format[] getFormatsByArgumentIndex();
method public java.util.Locale getLocale();
- method public java.lang.Object[] parse(java.lang.String) throws java.text.ParseException;
method public java.lang.Object[] parse(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Object[] parse(java.lang.String) throws java.text.ParseException;
method public java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
method public void setFormat(int, java.text.Format);
method public void setFormatByArgumentIndex(int, java.text.Format);
@@ -53196,11 +54376,11 @@
public abstract class NumberFormat extends java.text.Format {
ctor protected NumberFormat();
- method public final java.lang.String format(double);
- method public abstract java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
- method public final java.lang.String format(long);
- method public abstract java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+ method public final java.lang.String format(double);
+ method public final java.lang.String format(long);
+ method public abstract java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
+ method public abstract java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public static java.util.Locale[] getAvailableLocales();
method public java.util.Currency getCurrency();
method public static final java.text.NumberFormat getCurrencyInstance();
@@ -53220,8 +54400,8 @@
method public java.math.RoundingMode getRoundingMode();
method public boolean isGroupingUsed();
method public boolean isParseIntegerOnly();
- method public java.lang.Number parse(java.lang.String) throws java.text.ParseException;
method public abstract java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+ method public java.lang.Number parse(java.lang.String) throws java.text.ParseException;
method public final java.lang.Object parseObject(java.lang.String, java.text.ParsePosition);
method public void setCurrency(java.util.Currency);
method public void setGroupingUsed(boolean);
@@ -53265,10 +54445,10 @@
public class RuleBasedCollator extends java.text.Collator {
ctor public RuleBasedCollator(java.lang.String) throws java.text.ParseException;
- method public int compare(java.lang.String, java.lang.String);
- method public java.text.CollationElementIterator getCollationElementIterator(java.text.CharacterIterator);
+ method public synchronized int compare(java.lang.String, java.lang.String);
method public java.text.CollationElementIterator getCollationElementIterator(java.lang.String);
- method public java.text.CollationKey getCollationKey(java.lang.String);
+ method public java.text.CollationElementIterator getCollationElementIterator(java.text.CharacterIterator);
+ method public synchronized java.text.CollationKey getCollationKey(java.lang.String);
method public java.lang.String getRules();
method public int hashCode();
}
@@ -53276,8 +54456,8 @@
public class SimpleDateFormat extends java.text.DateFormat {
ctor public SimpleDateFormat();
ctor public SimpleDateFormat(java.lang.String);
- ctor public SimpleDateFormat(java.lang.String, java.text.DateFormatSymbols);
ctor public SimpleDateFormat(java.lang.String, java.util.Locale);
+ ctor public SimpleDateFormat(java.lang.String, java.text.DateFormatSymbols);
method public void applyLocalizedPattern(java.lang.String);
method public void applyPattern(java.lang.String);
method public java.lang.StringBuffer format(java.util.Date, java.lang.StringBuffer, java.text.FieldPosition);
@@ -53424,7 +54604,7 @@
method public int size();
}
- public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.RandomAccess java.io.Serializable {
+ public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
ctor public ArrayList(int);
ctor public ArrayList();
ctor public ArrayList(java.util.Collection<? extends E>);
@@ -53437,109 +54617,109 @@
public class Arrays {
method public static java.util.List<T> asList(T...);
- method public static int binarySearch(byte[], byte);
- method public static int binarySearch(byte[], int, int, byte);
+ method public static int binarySearch(long[], long);
+ method public static int binarySearch(long[], int, int, long);
+ method public static int binarySearch(int[], int);
+ method public static int binarySearch(int[], int, int, int);
+ method public static int binarySearch(short[], short);
+ method public static int binarySearch(short[], int, int, short);
method public static int binarySearch(char[], char);
method public static int binarySearch(char[], int, int, char);
+ method public static int binarySearch(byte[], byte);
+ method public static int binarySearch(byte[], int, int, byte);
method public static int binarySearch(double[], double);
method public static int binarySearch(double[], int, int, double);
method public static int binarySearch(float[], float);
method public static int binarySearch(float[], int, int, float);
- method public static int binarySearch(int[], int);
- method public static int binarySearch(int[], int, int, int);
- method public static int binarySearch(long[], long);
- method public static int binarySearch(long[], int, int, long);
method public static int binarySearch(java.lang.Object[], java.lang.Object);
method public static int binarySearch(java.lang.Object[], int, int, java.lang.Object);
method public static int binarySearch(T[], T, java.util.Comparator<? super T>);
method public static int binarySearch(T[], int, int, T, java.util.Comparator<? super T>);
- method public static int binarySearch(short[], short);
- method public static int binarySearch(short[], int, int, short);
- method public static boolean[] copyOf(boolean[], int);
- method public static byte[] copyOf(byte[], int);
- method public static char[] copyOf(char[], int);
- method public static double[] copyOf(double[], int);
- method public static float[] copyOf(float[], int);
- method public static int[] copyOf(int[], int);
- method public static long[] copyOf(long[], int);
- method public static short[] copyOf(short[], int);
method public static T[] copyOf(T[], int);
method public static T[] copyOf(U[], int, java.lang.Class<? extends T[]>);
- method public static boolean[] copyOfRange(boolean[], int, int);
- method public static byte[] copyOfRange(byte[], int, int);
- method public static char[] copyOfRange(char[], int, int);
- method public static double[] copyOfRange(double[], int, int);
- method public static float[] copyOfRange(float[], int, int);
- method public static int[] copyOfRange(int[], int, int);
- method public static long[] copyOfRange(long[], int, int);
- method public static short[] copyOfRange(short[], int, int);
+ method public static byte[] copyOf(byte[], int);
+ method public static short[] copyOf(short[], int);
+ method public static int[] copyOf(int[], int);
+ method public static long[] copyOf(long[], int);
+ method public static char[] copyOf(char[], int);
+ method public static float[] copyOf(float[], int);
+ method public static double[] copyOf(double[], int);
+ method public static boolean[] copyOf(boolean[], int);
method public static T[] copyOfRange(T[], int, int);
method public static T[] copyOfRange(U[], int, int, java.lang.Class<? extends T[]>);
+ method public static byte[] copyOfRange(byte[], int, int);
+ method public static short[] copyOfRange(short[], int, int);
+ method public static int[] copyOfRange(int[], int, int);
+ method public static long[] copyOfRange(long[], int, int);
+ method public static char[] copyOfRange(char[], int, int);
+ method public static float[] copyOfRange(float[], int, int);
+ method public static double[] copyOfRange(double[], int, int);
+ method public static boolean[] copyOfRange(boolean[], int, int);
method public static boolean deepEquals(java.lang.Object[], java.lang.Object[]);
method public static int deepHashCode(java.lang.Object[]);
method public static java.lang.String deepToString(java.lang.Object[]);
- method public static boolean equals(byte[], byte[]);
+ method public static boolean equals(long[], long[]);
+ method public static boolean equals(int[], int[]);
method public static boolean equals(short[], short[]);
method public static boolean equals(char[], char[]);
- method public static boolean equals(int[], int[]);
- method public static boolean equals(long[], long[]);
- method public static boolean equals(float[], float[]);
- method public static boolean equals(double[], double[]);
+ method public static boolean equals(byte[], byte[]);
method public static boolean equals(boolean[], boolean[]);
+ method public static boolean equals(double[], double[]);
+ method public static boolean equals(float[], float[]);
method public static boolean equals(java.lang.Object[], java.lang.Object[]);
- method public static void fill(byte[], byte);
- method public static void fill(byte[], int, int, byte);
+ method public static void fill(long[], long);
+ method public static void fill(long[], int, int, long);
+ method public static void fill(int[], int);
+ method public static void fill(int[], int, int, int);
method public static void fill(short[], short);
method public static void fill(short[], int, int, short);
method public static void fill(char[], char);
method public static void fill(char[], int, int, char);
- method public static void fill(int[], int);
- method public static void fill(int[], int, int, int);
- method public static void fill(long[], long);
- method public static void fill(long[], int, int, long);
- method public static void fill(float[], float);
- method public static void fill(float[], int, int, float);
- method public static void fill(double[], double);
- method public static void fill(double[], int, int, double);
+ method public static void fill(byte[], byte);
+ method public static void fill(byte[], int, int, byte);
method public static void fill(boolean[], boolean);
method public static void fill(boolean[], int, int, boolean);
+ method public static void fill(double[], double);
+ method public static void fill(double[], int, int, double);
+ method public static void fill(float[], float);
+ method public static void fill(float[], int, int, float);
method public static void fill(java.lang.Object[], java.lang.Object);
method public static void fill(java.lang.Object[], int, int, java.lang.Object);
- method public static int hashCode(boolean[]);
+ method public static int hashCode(long[]);
method public static int hashCode(int[]);
method public static int hashCode(short[]);
method public static int hashCode(char[]);
method public static int hashCode(byte[]);
- method public static int hashCode(long[]);
+ method public static int hashCode(boolean[]);
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
- method public static void sort(byte[]);
- method public static void sort(byte[], int, int);
- method public static void sort(char[]);
- method public static void sort(char[], int, int);
- method public static void sort(double[]);
- method public static void sort(double[], int, int);
- method public static void sort(float[]);
- method public static void sort(float[], int, int);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
method public static void sort(long[], int, int);
method public static void sort(short[]);
method public static void sort(short[], int, int);
+ method public static void sort(char[]);
+ method public static void sort(char[], int, int);
+ method public static void sort(byte[]);
+ method public static void sort(byte[], int, int);
+ method public static void sort(float[]);
+ method public static void sort(float[], int, int);
+ method public static void sort(double[]);
+ method public static void sort(double[], int, int);
method public static void sort(java.lang.Object[]);
method public static void sort(java.lang.Object[], int, int);
- method public static void sort(T[], int, int, java.util.Comparator<? super T>);
method public static void sort(T[], java.util.Comparator<? super T>);
- method public static java.lang.String toString(boolean[]);
- method public static java.lang.String toString(byte[]);
- method public static java.lang.String toString(char[]);
- method public static java.lang.String toString(double[]);
- method public static java.lang.String toString(float[]);
- method public static java.lang.String toString(int[]);
+ method public static void sort(T[], int, int, java.util.Comparator<? super T>);
method public static java.lang.String toString(long[]);
+ method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
+ method public static java.lang.String toString(char[]);
+ method public static java.lang.String toString(byte[]);
+ method public static java.lang.String toString(boolean[]);
+ method public static java.lang.String toString(float[]);
+ method public static java.lang.String toString(double[]);
method public static java.lang.String toString(java.lang.Object[]);
}
@@ -53550,8 +54730,8 @@
method public void andNot(java.util.BitSet);
method public int cardinality();
method public void clear(int);
- method public void clear();
method public void clear(int, int);
+ method public void clear();
method public java.lang.Object clone();
method public void flip(int);
method public void flip(int, int);
@@ -53567,8 +54747,8 @@
method public int previousSetBit(int);
method public void set(int);
method public void set(int, boolean);
- method public void set(int, int, boolean);
method public void set(int, int);
+ method public void set(int, int, boolean);
method public int size();
method public byte[] toByteArray();
method public long[] toLongArray();
@@ -53600,10 +54780,10 @@
method public java.util.Map<java.lang.String, java.lang.Integer> getDisplayNames(int, int, java.util.Locale);
method public int getFirstDayOfWeek();
method public abstract int getGreatestMinimum(int);
- method public static synchronized java.util.Calendar getInstance();
- method public static synchronized java.util.Calendar getInstance(java.util.Locale);
- method public static synchronized java.util.Calendar getInstance(java.util.TimeZone);
- method public static synchronized java.util.Calendar getInstance(java.util.TimeZone, java.util.Locale);
+ method public static java.util.Calendar getInstance();
+ method public static java.util.Calendar getInstance(java.util.TimeZone);
+ method public static java.util.Calendar getInstance(java.util.Locale);
+ method public static java.util.Calendar getInstance(java.util.TimeZone, java.util.Locale);
method public abstract int getLeastMaximum(int);
method public abstract int getMaximum(int);
method public int getMinimalDaysInFirstWeek();
@@ -53611,11 +54791,14 @@
method public final java.util.Date getTime();
method public long getTimeInMillis();
method public java.util.TimeZone getTimeZone();
+ method public int getWeekYear();
+ method public int getWeeksInWeekYear();
method protected final int internalGet(int);
method public boolean isLenient();
method public final boolean isSet(int);
- method public void roll(int, int);
+ method public boolean isWeekDateSupported();
method public abstract void roll(int, boolean);
+ method public void roll(int, int);
method public void set(int, int);
method public final void set(int, int, int);
method public final void set(int, int, int, int, int);
@@ -53626,6 +54809,7 @@
method public final void setTime(java.util.Date);
method public void setTimeInMillis(long);
method public void setTimeZone(java.util.TimeZone);
+ method public void setWeekDate(int, int, int);
field public static final int ALL_STYLES = 0; // 0x0
field public static final int AM = 0; // 0x0
field public static final int AM_PM = 9; // 0x9
@@ -53742,15 +54926,15 @@
method public static java.util.Collection<T> synchronizedCollection(java.util.Collection<T>);
method public static java.util.List<T> synchronizedList(java.util.List<T>);
method public static java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>);
- method public static java.util.Set<E> synchronizedSet(java.util.Set<E>);
+ method public static java.util.Set<T> synchronizedSet(java.util.Set<T>);
method public static java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>);
- method public static java.util.SortedSet<E> synchronizedSortedSet(java.util.SortedSet<E>);
- method public static java.util.Collection<E> unmodifiableCollection(java.util.Collection<? extends E>);
- method public static java.util.List<E> unmodifiableList(java.util.List<? extends E>);
+ method public static java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>);
+ method public static java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>);
+ method public static java.util.List<T> unmodifiableList(java.util.List<? extends T>);
method public static java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>);
- method public static java.util.Set<E> unmodifiableSet(java.util.Set<? extends E>);
+ method public static java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>);
method public static java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>);
- method public static java.util.SortedSet<E> unmodifiableSortedSet(java.util.SortedSet<E>);
+ method public static java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>);
field public static final java.util.List EMPTY_LIST;
field public static final java.util.Map EMPTY_MAP;
field public static final java.util.Set EMPTY_SET;
@@ -53764,8 +54948,8 @@
public class ConcurrentModificationException extends java.lang.RuntimeException {
ctor public ConcurrentModificationException();
ctor public ConcurrentModificationException(java.lang.String);
- ctor public ConcurrentModificationException(java.lang.String, java.lang.Throwable);
ctor public ConcurrentModificationException(java.lang.Throwable);
+ ctor public ConcurrentModificationException(java.lang.String, java.lang.Throwable);
}
public final class Currency implements java.io.Serializable {
@@ -53776,16 +54960,17 @@
method public java.lang.String getDisplayName(java.util.Locale);
method public static java.util.Currency getInstance(java.lang.String);
method public static java.util.Currency getInstance(java.util.Locale);
+ method public int getNumericCode();
method public java.lang.String getSymbol();
method public java.lang.String getSymbol(java.util.Locale);
}
public class Date implements java.lang.Cloneable java.lang.Comparable java.io.Serializable {
ctor public Date();
+ ctor public Date(long);
ctor public deprecated Date(int, int, int);
ctor public deprecated Date(int, int, int, int, int);
ctor public deprecated Date(int, int, int, int, int, int);
- ctor public Date(long);
ctor public deprecated Date(java.lang.String);
method public static deprecated long UTC(int, int, int, int, int, int);
method public boolean after(java.util.Date);
@@ -53863,7 +55048,7 @@
ctor public EmptyStackException();
}
- public class EnumMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
+ public class EnumMap extends java.util.AbstractMap implements java.lang.Cloneable java.io.Serializable {
ctor public EnumMap(java.lang.Class<K>);
ctor public EnumMap(java.util.EnumMap<K, ? extends V>);
ctor public EnumMap(java.util.Map<K, ? extends V>);
@@ -53896,8 +55081,8 @@
}
public abstract class EventListenerProxy implements java.util.EventListener {
- ctor public EventListenerProxy(java.util.EventListener);
- method public java.util.EventListener getListener();
+ ctor public EventListenerProxy(T);
+ method public T getListener();
}
public class EventObject implements java.io.Serializable {
@@ -53906,14 +55091,14 @@
field protected transient java.lang.Object source;
}
- public class FormatFlagsConversionMismatchException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class FormatFlagsConversionMismatchException extends java.util.IllegalFormatException {
ctor public FormatFlagsConversionMismatchException(java.lang.String, char);
method public char getConversion();
method public java.lang.String getFlags();
}
public abstract interface Formattable {
- method public abstract void formatTo(java.util.Formatter, int, int, int) throws java.util.IllegalFormatException;
+ method public abstract void formatTo(java.util.Formatter, int, int, int);
}
public class FormattableFlags {
@@ -53933,10 +55118,10 @@
ctor public Formatter(java.io.File) throws java.io.FileNotFoundException;
ctor public Formatter(java.io.File, java.lang.String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
ctor public Formatter(java.io.File, java.lang.String, java.util.Locale) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+ ctor public Formatter(java.io.PrintStream);
ctor public Formatter(java.io.OutputStream);
ctor public Formatter(java.io.OutputStream, java.lang.String) throws java.io.UnsupportedEncodingException;
ctor public Formatter(java.io.OutputStream, java.lang.String, java.util.Locale) throws java.io.UnsupportedEncodingException;
- ctor public Formatter(java.io.PrintStream);
method public void close();
method public void flush();
method public java.util.Formatter format(java.lang.String, java.lang.Object...);
@@ -53953,18 +55138,18 @@
enum_constant public static final java.util.Formatter.BigDecimalLayoutForm SCIENTIFIC;
}
- public class FormatterClosedException extends java.lang.IllegalStateException implements java.io.Serializable {
+ public class FormatterClosedException extends java.lang.IllegalStateException {
ctor public FormatterClosedException();
}
public class GregorianCalendar extends java.util.Calendar {
ctor public GregorianCalendar();
+ ctor public GregorianCalendar(java.util.TimeZone);
+ ctor public GregorianCalendar(java.util.Locale);
+ ctor public GregorianCalendar(java.util.TimeZone, java.util.Locale);
ctor public GregorianCalendar(int, int, int);
ctor public GregorianCalendar(int, int, int, int, int);
ctor public GregorianCalendar(int, int, int, int, int, int);
- ctor public GregorianCalendar(java.util.Locale);
- ctor public GregorianCalendar(java.util.TimeZone);
- ctor public GregorianCalendar(java.util.TimeZone, java.util.Locale);
method public void add(int, int);
method protected void computeFields();
method protected void computeTime();
@@ -53974,16 +55159,17 @@
method public int getMaximum(int);
method public int getMinimum(int);
method public boolean isLeapYear(int);
+ method public final boolean isWeekDateSupported();
method public void roll(int, boolean);
method public void setGregorianChange(java.util.Date);
field public static final int AD = 1; // 0x1
field public static final int BC = 0; // 0x0
}
- public class HashMap extends java.util.AbstractMap implements java.lang.Cloneable java.io.Serializable {
- ctor public HashMap();
- ctor public HashMap(int);
+ public class HashMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
ctor public HashMap(int, float);
+ ctor public HashMap(int);
+ ctor public HashMap();
ctor public HashMap(java.util.Map<? extends K, ? extends V>);
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
@@ -53991,36 +55177,36 @@
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
ctor public HashSet();
- ctor public HashSet(int);
- ctor public HashSet(int, float);
ctor public HashSet(java.util.Collection<? extends E>);
+ ctor public HashSet(int, float);
+ ctor public HashSet(int);
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
- ctor public Hashtable();
- ctor public Hashtable(int);
ctor public Hashtable(int, float);
+ ctor public Hashtable(int);
+ ctor public Hashtable();
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
- method public boolean contains(java.lang.Object);
+ method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
- method public synchronized boolean containsValue(java.lang.Object);
+ method public boolean containsValue(java.lang.Object);
method public synchronized java.util.Enumeration<V> elements();
- method public synchronized java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+ method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized V get(java.lang.Object);
method public synchronized boolean isEmpty();
- method public synchronized java.util.Set<K> keySet();
+ method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
method public synchronized int size();
- method public synchronized java.util.Collection<V> values();
+ method public java.util.Collection<V> values();
}
public class IdentityHashMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -54031,21 +55217,21 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
}
- public class IllegalFormatCodePointException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatCodePointException extends java.util.IllegalFormatException {
ctor public IllegalFormatCodePointException(int);
method public int getCodePoint();
}
- public class IllegalFormatConversionException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatConversionException extends java.util.IllegalFormatException {
ctor public IllegalFormatConversionException(char, java.lang.Class<?>);
method public java.lang.Class<?> getArgumentClass();
method public char getConversion();
}
- public class IllegalFormatException extends java.lang.IllegalArgumentException implements java.io.Serializable {
+ public class IllegalFormatException extends java.lang.IllegalArgumentException {
}
- public class IllegalFormatFlagsException extends java.util.IllegalFormatException implements java.io.Serializable {
+ public class IllegalFormatFlagsException extends java.util.IllegalFormatException {
ctor public IllegalFormatFlagsException(java.lang.String);
method public java.lang.String getFlags();
}
@@ -54067,14 +55253,14 @@
method public int getErrorIndex();
}
- public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable {
+ public class InputMismatchException extends java.util.NoSuchElementException {
ctor public InputMismatchException();
ctor public InputMismatchException(java.lang.String);
}
public class InvalidPropertiesFormatException extends java.io.IOException {
- ctor public InvalidPropertiesFormatException(java.lang.String);
ctor public InvalidPropertiesFormatException(java.lang.Throwable);
+ ctor public InvalidPropertiesFormatException(java.lang.String);
}
public abstract interface Iterator {
@@ -54083,23 +55269,23 @@
method public abstract void remove();
}
- public class LinkedHashMap extends java.util.HashMap {
- ctor public LinkedHashMap();
- ctor public LinkedHashMap(int);
+ public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
ctor public LinkedHashMap(int, float);
- ctor public LinkedHashMap(int, float, boolean);
+ ctor public LinkedHashMap(int);
+ ctor public LinkedHashMap();
ctor public LinkedHashMap(java.util.Map<? extends K, ? extends V>);
+ ctor public LinkedHashMap(int, float, boolean);
method protected boolean removeEldestEntry(java.util.Map.Entry<K, V>);
}
public class LinkedHashSet extends java.util.HashSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
- ctor public LinkedHashSet();
- ctor public LinkedHashSet(int);
ctor public LinkedHashSet(int, float);
+ ctor public LinkedHashSet(int);
+ ctor public LinkedHashSet();
ctor public LinkedHashSet(java.util.Collection<? extends E>);
}
- public class LinkedList extends java.util.AbstractSequentialList implements java.lang.Cloneable java.util.Deque java.util.List java.util.Queue java.io.Serializable {
+ public class LinkedList extends java.util.AbstractSequentialList implements java.lang.Cloneable java.util.Deque java.util.List java.io.Serializable {
ctor public LinkedList();
ctor public LinkedList(java.util.Collection<? extends E>);
method public void addFirst(E);
@@ -54130,10 +55316,10 @@
}
public abstract interface List implements java.util.Collection {
- method public abstract void add(int, E);
method public abstract boolean add(E);
- method public abstract boolean addAll(int, java.util.Collection<? extends E>);
+ method public abstract void add(int, E);
method public abstract boolean addAll(java.util.Collection<? extends E>);
+ method public abstract boolean addAll(int, java.util.Collection<? extends E>);
method public abstract void clear();
method public abstract boolean contains(java.lang.Object);
method public abstract boolean containsAll(java.util.Collection<?>);
@@ -54146,8 +55332,8 @@
method public abstract int lastIndexOf(java.lang.Object);
method public abstract java.util.ListIterator<E> listIterator();
method public abstract java.util.ListIterator<E> listIterator(int);
- method public abstract E remove(int);
method public abstract boolean remove(java.lang.Object);
+ method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
@@ -54177,14 +55363,15 @@
}
public final class Locale implements java.lang.Cloneable java.io.Serializable {
- ctor public Locale(java.lang.String);
- ctor public Locale(java.lang.String, java.lang.String);
ctor public Locale(java.lang.String, java.lang.String, java.lang.String);
+ ctor public Locale(java.lang.String, java.lang.String);
+ ctor public Locale(java.lang.String);
method public java.lang.Object clone();
method public static java.util.Locale forLanguageTag(java.lang.String);
method public static java.util.Locale[] getAvailableLocales();
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
+ method public static java.util.Locale getDefault(java.util.Locale.Category);
method public final java.lang.String getDisplayCountry();
method public java.lang.String getDisplayCountry(java.util.Locale);
method public final java.lang.String getDisplayLanguage();
@@ -54197,8 +55384,8 @@
method public java.lang.String getDisplayVariant(java.util.Locale);
method public java.lang.String getExtension(char);
method public java.util.Set<java.lang.Character> getExtensionKeys();
- method public java.lang.String getISO3Country();
- method public java.lang.String getISO3Language();
+ method public java.lang.String getISO3Country() throws java.util.MissingResourceException;
+ method public java.lang.String getISO3Language() throws java.util.MissingResourceException;
method public static java.lang.String[] getISOCountries();
method public static java.lang.String[] getISOLanguages();
method public java.lang.String getLanguage();
@@ -54208,6 +55395,7 @@
method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
method public static synchronized void setDefault(java.util.Locale);
+ method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
@@ -54253,6 +55441,13 @@
method public java.util.Locale.Builder setVariant(java.lang.String);
}
+ public static final class Locale.Category extends java.lang.Enum {
+ method public static java.util.Locale.Category valueOf(java.lang.String);
+ method public static final java.util.Locale.Category[] values();
+ enum_constant public static final java.util.Locale.Category DISPLAY;
+ enum_constant public static final java.util.Locale.Category FORMAT;
+ }
+
public abstract interface Map {
method public abstract void clear();
method public abstract boolean containsKey(java.lang.Object);
@@ -54355,15 +55550,15 @@
public class Observable {
ctor public Observable();
- method public void addObserver(java.util.Observer);
- method protected void clearChanged();
- method public int countObservers();
+ method public synchronized void addObserver(java.util.Observer);
+ method protected synchronized void clearChanged();
+ method public synchronized int countObservers();
method public synchronized void deleteObserver(java.util.Observer);
method public synchronized void deleteObservers();
- method public boolean hasChanged();
+ method public synchronized boolean hasChanged();
method public void notifyObservers();
method public void notifyObservers(java.lang.Object);
- method protected void setChanged();
+ method protected synchronized void setChanged();
}
public abstract interface Observer {
@@ -54392,16 +55587,16 @@
method public java.lang.String getProperty(java.lang.String, java.lang.String);
method public void list(java.io.PrintStream);
method public void list(java.io.PrintWriter);
- method public synchronized void load(java.io.InputStream) throws java.io.IOException;
method public synchronized void load(java.io.Reader) throws java.io.IOException;
+ method public synchronized void load(java.io.InputStream) throws java.io.IOException;
method public synchronized void loadFromXML(java.io.InputStream) throws java.io.IOException, java.util.InvalidPropertiesFormatException;
method public java.util.Enumeration<?> propertyNames();
method public deprecated void save(java.io.OutputStream, java.lang.String);
- method public java.lang.Object setProperty(java.lang.String, java.lang.String);
- method public synchronized void store(java.io.OutputStream, java.lang.String) throws java.io.IOException;
- method public synchronized void store(java.io.Writer, java.lang.String) throws java.io.IOException;
+ method public synchronized java.lang.Object setProperty(java.lang.String, java.lang.String);
+ method public void store(java.io.Writer, java.lang.String) throws java.io.IOException;
+ method public void store(java.io.OutputStream, java.lang.String) throws java.io.IOException;
method public void storeToXML(java.io.OutputStream, java.lang.String) throws java.io.IOException;
- method public synchronized void storeToXML(java.io.OutputStream, java.lang.String, java.lang.String) throws java.io.IOException;
+ method public void storeToXML(java.io.OutputStream, java.lang.String, java.lang.String) throws java.io.IOException;
method public java.util.Set<java.lang.String> stringPropertyNames();
field protected java.util.Properties defaults;
}
@@ -54429,7 +55624,7 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
- method protected synchronized int next(int);
+ method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
method public double nextDouble();
@@ -54446,14 +55641,14 @@
public abstract class ResourceBundle {
ctor public ResourceBundle();
- method public static void clearCache();
- method public static void clearCache(java.lang.ClassLoader);
+ method public static final void clearCache();
+ method public static final void clearCache(java.lang.ClassLoader);
method public boolean containsKey(java.lang.String);
- method public static java.util.ResourceBundle getBundle(java.lang.String) throws java.util.MissingResourceException;
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale);
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader) throws java.util.MissingResourceException;
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.ResourceBundle.Control);
- method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.util.ResourceBundle.Control);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.ResourceBundle.Control);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale);
+ method public static final java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.util.ResourceBundle.Control);
+ method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader);
method public static java.util.ResourceBundle getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader, java.util.ResourceBundle.Control);
method public abstract java.util.Enumeration<java.lang.String> getKeys();
method public java.util.Locale getLocale();
@@ -54470,10 +55665,10 @@
public static class ResourceBundle.Control {
ctor protected ResourceBundle.Control();
method public java.util.List<java.util.Locale> getCandidateLocales(java.lang.String, java.util.Locale);
- method public static java.util.ResourceBundle.Control getControl(java.util.List<java.lang.String>);
+ method public static final java.util.ResourceBundle.Control getControl(java.util.List<java.lang.String>);
method public java.util.Locale getFallbackLocale(java.lang.String, java.util.Locale);
method public java.util.List<java.lang.String> getFormats(java.lang.String);
- method public static java.util.ResourceBundle.Control getNoFallbackControl(java.util.List<java.lang.String>);
+ method public static final java.util.ResourceBundle.Control getNoFallbackControl(java.util.List<java.lang.String>);
method public long getTimeToLive(java.lang.String, java.util.Locale);
method public boolean needsReload(java.lang.String, java.util.Locale, java.lang.String, java.lang.ClassLoader, java.util.ResourceBundle, long);
method public java.util.ResourceBundle newBundle(java.lang.String, java.util.Locale, java.lang.String, java.lang.ClassLoader, boolean) throws java.io.IOException, java.lang.IllegalAccessException, java.lang.InstantiationException;
@@ -54487,23 +55682,25 @@
}
public final class Scanner implements java.io.Closeable java.util.Iterator {
- ctor public Scanner(java.io.File) throws java.io.FileNotFoundException;
- ctor public Scanner(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
- ctor public Scanner(java.lang.String);
+ ctor public Scanner(java.lang.Readable);
ctor public Scanner(java.io.InputStream);
ctor public Scanner(java.io.InputStream, java.lang.String);
- ctor public Scanner(java.lang.Readable);
+ ctor public Scanner(java.io.File) throws java.io.FileNotFoundException;
+ ctor public Scanner(java.io.File, java.lang.String) throws java.io.FileNotFoundException;
+ ctor public Scanner(java.nio.file.Path) throws java.io.IOException;
+ ctor public Scanner(java.nio.file.Path, java.lang.String) throws java.io.IOException;
+ ctor public Scanner(java.lang.String);
ctor public Scanner(java.nio.channels.ReadableByteChannel);
ctor public Scanner(java.nio.channels.ReadableByteChannel, java.lang.String);
method public void close();
method public java.util.regex.Pattern delimiter();
- method public java.lang.String findInLine(java.util.regex.Pattern);
method public java.lang.String findInLine(java.lang.String);
- method public java.lang.String findWithinHorizon(java.util.regex.Pattern, int);
+ method public java.lang.String findInLine(java.util.regex.Pattern);
method public java.lang.String findWithinHorizon(java.lang.String, int);
+ method public java.lang.String findWithinHorizon(java.util.regex.Pattern, int);
method public boolean hasNext();
- method public boolean hasNext(java.util.regex.Pattern);
method public boolean hasNext(java.lang.String);
+ method public boolean hasNext(java.util.regex.Pattern);
method public boolean hasNextBigDecimal();
method public boolean hasNextBigInteger();
method public boolean hasNextBigInteger(int);
@@ -54523,8 +55720,8 @@
method public java.util.Locale locale();
method public java.util.regex.MatchResult match();
method public java.lang.String next();
- method public java.lang.String next(java.util.regex.Pattern);
method public java.lang.String next(java.lang.String);
+ method public java.lang.String next(java.util.regex.Pattern);
method public java.math.BigDecimal nextBigDecimal();
method public java.math.BigInteger nextBigInteger();
method public java.math.BigInteger nextBigInteger(int);
@@ -54591,12 +55788,12 @@
method public int getRawOffset();
method public boolean inDaylightTime(java.util.Date);
method public void setDSTSavings(int);
- method public void setEndRule(int, int, int);
method public void setEndRule(int, int, int, int);
+ method public void setEndRule(int, int, int);
method public void setEndRule(int, int, int, int, boolean);
method public void setRawOffset(int);
- method public void setStartRule(int, int, int);
method public void setStartRule(int, int, int, int);
+ method public void setStartRule(int, int, int);
method public void setStartRule(int, int, int, int, boolean);
method public void setStartYear(int);
method public boolean useDaylightTime();
@@ -54607,11 +55804,14 @@
public abstract interface SortedMap implements java.util.Map {
method public abstract java.util.Comparator<? super K> comparator();
+ method public abstract java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public abstract K firstKey();
method public abstract java.util.SortedMap<K, V> headMap(K);
+ method public abstract java.util.Set<K> keySet();
method public abstract K lastKey();
method public abstract java.util.SortedMap<K, V> subMap(K, K);
method public abstract java.util.SortedMap<K, V> tailMap(K);
+ method public abstract java.util.Collection<V> values();
}
public abstract interface SortedSet implements java.util.Set {
@@ -54633,9 +55833,9 @@
}
public class StringTokenizer implements java.util.Enumeration {
- ctor public StringTokenizer(java.lang.String);
- ctor public StringTokenizer(java.lang.String, java.lang.String);
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
+ ctor public StringTokenizer(java.lang.String, java.lang.String);
+ ctor public StringTokenizer(java.lang.String);
method public int countTokens();
method public boolean hasMoreElements();
method public boolean hasMoreTokens();
@@ -54647,21 +55847,22 @@
public abstract class TimeZone implements java.lang.Cloneable java.io.Serializable {
ctor public TimeZone();
method public java.lang.Object clone();
- method public static synchronized java.lang.String[] getAvailableIDs();
method public static synchronized java.lang.String[] getAvailableIDs(int);
+ method public static synchronized java.lang.String[] getAvailableIDs();
method public int getDSTSavings();
- method public static synchronized java.util.TimeZone getDefault();
+ method public static java.util.TimeZone getDefault();
method public final java.lang.String getDisplayName();
method public final java.lang.String getDisplayName(java.util.Locale);
method public final java.lang.String getDisplayName(boolean, int);
method public java.lang.String getDisplayName(boolean, int, java.util.Locale);
method public java.lang.String getID();
- method public int getOffset(long);
method public abstract int getOffset(int, int, int, int, int, int);
+ method public int getOffset(long);
method public abstract int getRawOffset();
method public static synchronized java.util.TimeZone getTimeZone(java.lang.String);
method public boolean hasSameRules(java.util.TimeZone);
method public abstract boolean inDaylightTime(java.util.Date);
+ method public boolean observesDaylightTime();
method public static synchronized void setDefault(java.util.TimeZone);
method public void setID(java.lang.String);
method public abstract void setRawOffset(int);
@@ -54671,14 +55872,14 @@
}
public class Timer {
- ctor public Timer(java.lang.String, boolean);
- ctor public Timer(java.lang.String);
- ctor public Timer(boolean);
ctor public Timer();
+ ctor public Timer(boolean);
+ ctor public Timer(java.lang.String);
+ ctor public Timer(java.lang.String, boolean);
method public void cancel();
method public int purge();
- method public void schedule(java.util.TimerTask, java.util.Date);
method public void schedule(java.util.TimerTask, long);
+ method public void schedule(java.util.TimerTask, java.util.Date);
method public void schedule(java.util.TimerTask, long, long);
method public void schedule(java.util.TimerTask, java.util.Date, long);
method public void scheduleAtFixedRate(java.util.TimerTask, long, long);
@@ -54697,10 +55898,10 @@
ctor public TooManyListenersException(java.lang.String);
}
- public class TreeMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.NavigableMap java.io.Serializable java.util.SortedMap {
+ public class TreeMap extends java.util.AbstractMap implements java.lang.Cloneable java.util.NavigableMap java.io.Serializable {
ctor public TreeMap();
- ctor public TreeMap(java.util.Map<? extends K, ? extends V>);
ctor public TreeMap(java.util.Comparator<? super K>);
+ ctor public TreeMap(java.util.Map<? extends K, ? extends V>);
ctor public TreeMap(java.util.SortedMap<K, ? extends V>);
method public java.util.Map.Entry<K, V> ceilingEntry(K);
method public K ceilingKey(K);
@@ -54732,8 +55933,8 @@
public class TreeSet extends java.util.AbstractSet implements java.lang.Cloneable java.util.NavigableSet java.io.Serializable {
ctor public TreeSet();
- ctor public TreeSet(java.util.Collection<? extends E>);
ctor public TreeSet(java.util.Comparator<? super E>);
+ ctor public TreeSet(java.util.Collection<? extends E>);
ctor public TreeSet(java.util.SortedSet<E>);
method public E ceiling(E);
method public java.lang.Object clone();
@@ -54783,9 +55984,9 @@
}
public class Vector extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
- ctor public Vector();
- ctor public Vector(int);
ctor public Vector(int, int);
+ ctor public Vector(int);
+ ctor public Vector();
ctor public Vector(java.util.Collection<? extends E>);
method public synchronized void addElement(E);
method public synchronized int capacity();
@@ -54795,7 +55996,7 @@
method public java.util.Enumeration<E> elements();
method public synchronized void ensureCapacity(int);
method public synchronized E firstElement();
- method public E get(int);
+ method public synchronized E get(int);
method public synchronized int indexOf(java.lang.Object, int);
method public synchronized void insertElementAt(E, int);
method public synchronized E lastElement();
@@ -54813,9 +56014,9 @@
}
public class WeakHashMap extends java.util.AbstractMap implements java.util.Map {
- ctor public WeakHashMap();
- ctor public WeakHashMap(int);
ctor public WeakHashMap(int, float);
+ ctor public WeakHashMap(int);
+ ctor public WeakHashMap();
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
}
@@ -56053,16 +57254,16 @@
public class Attributes implements java.lang.Cloneable java.util.Map {
ctor public Attributes();
- ctor public Attributes(java.util.jar.Attributes);
ctor public Attributes(int);
+ ctor public Attributes(java.util.jar.Attributes);
method public void clear();
method public java.lang.Object clone();
method public boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
method public java.util.Set<java.util.Map.Entry<java.lang.Object, java.lang.Object>> entrySet();
method public java.lang.Object get(java.lang.Object);
- method public java.lang.String getValue(java.util.jar.Attributes.Name);
method public java.lang.String getValue(java.lang.String);
+ method public java.lang.String getValue(java.util.jar.Attributes.Name);
method public boolean isEmpty();
method public java.util.Set<java.lang.Object> keySet();
method public java.lang.Object put(java.lang.Object, java.lang.Object);
@@ -56110,19 +57311,20 @@
}
public class JarFile extends java.util.zip.ZipFile {
+ ctor public JarFile(java.lang.String) throws java.io.IOException;
+ ctor public JarFile(java.lang.String, boolean) throws java.io.IOException;
ctor public JarFile(java.io.File) throws java.io.IOException;
ctor public JarFile(java.io.File, boolean) throws java.io.IOException;
ctor public JarFile(java.io.File, boolean, int) throws java.io.IOException;
- ctor public JarFile(java.lang.String) throws java.io.IOException;
- ctor public JarFile(java.lang.String, boolean) throws java.io.IOException;
method public java.util.jar.JarEntry getJarEntry(java.lang.String);
method public java.util.jar.Manifest getManifest() throws java.io.IOException;
+ method public boolean hasClassPathAttribute() throws java.io.IOException;
field public static final java.lang.String MANIFEST_NAME = "META-INF/MANIFEST.MF";
}
public class JarInputStream extends java.util.zip.ZipInputStream {
- ctor public JarInputStream(java.io.InputStream, boolean) throws java.io.IOException;
ctor public JarInputStream(java.io.InputStream) throws java.io.IOException;
+ ctor public JarInputStream(java.io.InputStream, boolean) throws java.io.IOException;
method public java.util.jar.Manifest getManifest();
method public java.util.jar.JarEntry getNextJarEntry() throws java.io.IOException;
}
@@ -56146,7 +57348,7 @@
}
public abstract class Pack200 {
- method public static java.util.jar.Pack200.Packer newPacker();
+ method public static synchronized java.util.jar.Pack200.Packer newPacker();
method public static java.util.jar.Pack200.Unpacker newUnpacker();
}
@@ -56200,7 +57402,7 @@
public class ErrorManager {
ctor public ErrorManager();
- method public void error(java.lang.String, java.lang.Exception, int);
+ method public synchronized void error(java.lang.String, java.lang.Exception, int);
field public static final int CLOSE_FAILURE = 3; // 0x3
field public static final int FLUSH_FAILURE = 2; // 0x2
field public static final int FORMAT_FAILURE = 5; // 0x5
@@ -56210,11 +57412,11 @@
}
public class FileHandler extends java.util.logging.StreamHandler {
- ctor public FileHandler() throws java.io.IOException;
- ctor public FileHandler(java.lang.String) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, boolean) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, int, int) throws java.io.IOException;
- ctor public FileHandler(java.lang.String, int, int, boolean) throws java.io.IOException;
+ ctor public FileHandler() throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, boolean) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, int, int) throws java.io.IOException, java.lang.SecurityException;
+ ctor public FileHandler(java.lang.String, int, int, boolean) throws java.io.IOException, java.lang.SecurityException;
}
public abstract interface Filter {
@@ -56224,28 +57426,28 @@
public abstract class Formatter {
ctor protected Formatter();
method public abstract java.lang.String format(java.util.logging.LogRecord);
- method public java.lang.String formatMessage(java.util.logging.LogRecord);
+ method public synchronized java.lang.String formatMessage(java.util.logging.LogRecord);
method public java.lang.String getHead(java.util.logging.Handler);
method public java.lang.String getTail(java.util.logging.Handler);
}
public abstract class Handler {
ctor protected Handler();
- method public abstract void close();
+ method public abstract void close() throws java.lang.SecurityException;
method public abstract void flush();
method public java.lang.String getEncoding();
method public java.util.logging.ErrorManager getErrorManager();
method public java.util.logging.Filter getFilter();
method public java.util.logging.Formatter getFormatter();
- method public java.util.logging.Level getLevel();
+ method public synchronized java.util.logging.Level getLevel();
method public boolean isLoggable(java.util.logging.LogRecord);
method public abstract void publish(java.util.logging.LogRecord);
method protected void reportError(java.lang.String, java.lang.Exception, int);
- method public void setEncoding(java.lang.String) throws java.io.UnsupportedEncodingException;
+ method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
method public void setErrorManager(java.util.logging.ErrorManager);
- method public void setFilter(java.util.logging.Filter);
- method public void setFormatter(java.util.logging.Formatter);
- method public void setLevel(java.util.logging.Level);
+ method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+ method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class Level implements java.io.Serializable {
@@ -56255,7 +57457,7 @@
method public java.lang.String getName();
method public java.lang.String getResourceBundleName();
method public final int intValue();
- method public static java.util.logging.Level parse(java.lang.String) throws java.lang.IllegalArgumentException;
+ method public static synchronized java.util.logging.Level parse(java.lang.String) throws java.lang.IllegalArgumentException;
method public final java.lang.String toString();
field public static final java.util.logging.Level ALL;
field public static final java.util.logging.Level CONFIG;
@@ -56270,18 +57472,18 @@
public class LogManager {
ctor protected LogManager();
- method public synchronized boolean addLogger(java.util.logging.Logger);
- method public void addPropertyChangeListener(java.beans.PropertyChangeListener);
- method public void checkAccess();
+ method public boolean addLogger(java.util.logging.Logger);
+ method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public void checkAccess() throws java.lang.SecurityException;
method public static java.util.logging.LogManager getLogManager();
- method public synchronized java.util.logging.Logger getLogger(java.lang.String);
- method public synchronized java.util.Enumeration<java.lang.String> getLoggerNames();
- method public static java.util.logging.LoggingMXBean getLoggingMXBean();
+ method public java.util.logging.Logger getLogger(java.lang.String);
+ method public java.util.Enumeration<java.lang.String> getLoggerNames();
+ method public static synchronized java.util.logging.LoggingMXBean getLoggingMXBean();
method public java.lang.String getProperty(java.lang.String);
- method public void readConfiguration() throws java.io.IOException;
- method public void readConfiguration(java.io.InputStream) throws java.io.IOException;
- method public void removePropertyChangeListener(java.beans.PropertyChangeListener);
- method public synchronized void reset();
+ method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
+ method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
+ method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public void reset() throws java.lang.SecurityException;
field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
}
@@ -56315,7 +57517,7 @@
public class Logger {
ctor protected Logger(java.lang.String, java.lang.String);
- method public void addHandler(java.util.logging.Handler);
+ method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void config(java.lang.String);
method public void entering(java.lang.String, java.lang.String);
method public void entering(java.lang.String, java.lang.String, java.lang.Object);
@@ -56328,7 +57530,7 @@
method public static java.util.logging.Logger getAnonymousLogger();
method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
method public java.util.logging.Filter getFilter();
- method public static java.util.logging.Logger getGlobal();
+ method public static final java.util.logging.Logger getGlobal();
method public java.util.logging.Handler[] getHandlers();
method public java.util.logging.Level getLevel();
method public static java.util.logging.Logger getLogger(java.lang.String);
@@ -56340,11 +57542,11 @@
method public boolean getUseParentHandlers();
method public void info(java.lang.String);
method public boolean isLoggable(java.util.logging.Level);
+ method public void log(java.util.logging.LogRecord);
method public void log(java.util.logging.Level, java.lang.String);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
- method public void log(java.util.logging.LogRecord);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
@@ -56353,9 +57555,9 @@
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
- method public void removeHandler(java.util.logging.Handler);
- method public void setFilter(java.util.logging.Filter);
- method public void setLevel(java.util.logging.Level);
+ method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
+ method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
method public void setParent(java.util.logging.Logger);
method public void setUseParentHandlers(boolean);
method public void severe(java.lang.String);
@@ -56372,24 +57574,24 @@
method public abstract void setLoggerLevel(java.lang.String, java.lang.String);
}
- public final class LoggingPermission extends java.security.BasicPermission implements java.security.Guard java.io.Serializable {
- ctor public LoggingPermission(java.lang.String, java.lang.String);
+ public final class LoggingPermission extends java.security.BasicPermission {
+ ctor public LoggingPermission(java.lang.String, java.lang.String) throws java.lang.IllegalArgumentException;
}
public class MemoryHandler extends java.util.logging.Handler {
ctor public MemoryHandler();
ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
- method public void close();
+ method public void close() throws java.lang.SecurityException;
method public void flush();
- method public java.util.logging.Level getPushLevel();
+ method public synchronized java.util.logging.Level getPushLevel();
method public synchronized void publish(java.util.logging.LogRecord);
- method public void push();
- method public void setPushLevel(java.util.logging.Level);
+ method public synchronized void push();
+ method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class SimpleFormatter extends java.util.logging.Formatter {
ctor public SimpleFormatter();
- method public java.lang.String format(java.util.logging.LogRecord);
+ method public synchronized java.lang.String format(java.util.logging.LogRecord);
}
public class SocketHandler extends java.util.logging.StreamHandler {
@@ -56400,10 +57602,10 @@
public class StreamHandler extends java.util.logging.Handler {
ctor public StreamHandler();
ctor public StreamHandler(java.io.OutputStream, java.util.logging.Formatter);
- method public void close();
- method public void flush();
+ method public synchronized void close() throws java.lang.SecurityException;
+ method public synchronized void flush();
method public synchronized void publish(java.util.logging.LogRecord);
- method protected void setOutputStream(java.io.OutputStream);
+ method protected synchronized void setOutputStream(java.io.OutputStream) throws java.lang.SecurityException;
}
public class XMLFormatter extends java.util.logging.Formatter {
@@ -56473,12 +57675,12 @@
}
public class InvalidPreferencesFormatException extends java.lang.Exception {
+ ctor public InvalidPreferencesFormatException(java.lang.Throwable);
ctor public InvalidPreferencesFormatException(java.lang.String);
ctor public InvalidPreferencesFormatException(java.lang.String, java.lang.Throwable);
- ctor public InvalidPreferencesFormatException(java.lang.Throwable);
}
- public class NodeChangeEvent extends java.util.EventObject implements java.io.Serializable {
+ public class NodeChangeEvent extends java.util.EventObject {
ctor public NodeChangeEvent(java.util.prefs.Preferences, java.util.prefs.Preferences);
method public java.util.prefs.Preferences getChild();
method public java.util.prefs.Preferences getParent();
@@ -56489,7 +57691,7 @@
method public abstract void childRemoved(java.util.prefs.NodeChangeEvent);
}
- public class PreferenceChangeEvent extends java.util.EventObject implements java.io.Serializable {
+ public class PreferenceChangeEvent extends java.util.EventObject {
ctor public PreferenceChangeEvent(java.util.prefs.Preferences, java.lang.String, java.lang.String);
method public java.lang.String getKey();
method public java.lang.String getNewValue();
@@ -56634,8 +57836,8 @@
method public long getValue();
method public void reset();
method public void update(int);
- method public void update(byte[]);
method public void update(byte[], int, int);
+ method public void update(byte[]);
}
public class CRC32 implements java.util.zip.Checksum {
@@ -56643,8 +57845,8 @@
method public long getValue();
method public void reset();
method public void update(int);
- method public void update(byte[]);
method public void update(byte[], int, int);
+ method public void update(byte[]);
}
public class CheckedInputStream extends java.io.FilterInputStream {
@@ -56660,8 +57862,8 @@
public abstract interface Checksum {
method public abstract long getValue();
method public abstract void reset();
- method public abstract void update(byte[], int, int);
method public abstract void update(int);
+ method public abstract void update(byte[], int, int);
}
public class DataFormatException extends java.lang.Exception {
@@ -56670,28 +57872,28 @@
}
public class Deflater {
- ctor public Deflater();
- ctor public Deflater(int);
ctor public Deflater(int, boolean);
+ ctor public Deflater(int);
+ ctor public Deflater();
+ method public int deflate(byte[], int, int);
method public int deflate(byte[]);
- method public synchronized int deflate(byte[], int, int);
- method public synchronized int deflate(byte[], int, int, int);
- method public synchronized void end();
- method public synchronized void finish();
- method public synchronized boolean finished();
- method public synchronized int getAdler();
- method public synchronized long getBytesRead();
- method public synchronized long getBytesWritten();
- method public synchronized int getTotalIn();
- method public synchronized int getTotalOut();
- method public synchronized boolean needsInput();
- method public synchronized void reset();
+ method public int deflate(byte[], int, int, int);
+ method public void end();
+ method public void finish();
+ method public boolean finished();
+ method public int getAdler();
+ method public long getBytesRead();
+ method public long getBytesWritten();
+ method public int getTotalIn();
+ method public int getTotalOut();
+ method public boolean needsInput();
+ method public void reset();
+ method public void setDictionary(byte[], int, int);
method public void setDictionary(byte[]);
- method public synchronized void setDictionary(byte[], int, int);
+ method public void setInput(byte[], int, int);
method public void setInput(byte[]);
- method public synchronized void setInput(byte[], int, int);
- method public synchronized void setLevel(int);
- method public synchronized void setStrategy(int);
+ method public void setLevel(int);
+ method public void setStrategy(int);
field public static final int BEST_COMPRESSION = 9; // 0x9
field public static final int BEST_SPEED = 1; // 0x1
field public static final int DEFAULT_COMPRESSION = -1; // 0xffffffff
@@ -56714,12 +57916,12 @@
}
public class DeflaterOutputStream extends java.io.FilterOutputStream {
- ctor public DeflaterOutputStream(java.io.OutputStream);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int);
- ctor public DeflaterOutputStream(java.io.OutputStream, boolean);
- ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, boolean);
ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater);
+ ctor public DeflaterOutputStream(java.io.OutputStream, boolean);
+ ctor public DeflaterOutputStream(java.io.OutputStream);
method protected void deflate() throws java.io.IOException;
method public void finish() throws java.io.IOException;
field protected byte[] buf;
@@ -56727,49 +57929,50 @@
}
public class GZIPInputStream extends java.util.zip.InflaterInputStream {
- ctor public GZIPInputStream(java.io.InputStream) throws java.io.IOException;
ctor public GZIPInputStream(java.io.InputStream, int) throws java.io.IOException;
+ ctor public GZIPInputStream(java.io.InputStream) throws java.io.IOException;
field public static final int GZIP_MAGIC = 35615; // 0x8b1f
field protected java.util.zip.CRC32 crc;
field protected boolean eos;
}
public class GZIPOutputStream extends java.util.zip.DeflaterOutputStream {
- ctor public GZIPOutputStream(java.io.OutputStream) throws java.io.IOException;
- ctor public GZIPOutputStream(java.io.OutputStream, boolean) throws java.io.IOException;
ctor public GZIPOutputStream(java.io.OutputStream, int) throws java.io.IOException;
ctor public GZIPOutputStream(java.io.OutputStream, int, boolean) throws java.io.IOException;
+ ctor public GZIPOutputStream(java.io.OutputStream) throws java.io.IOException;
+ ctor public GZIPOutputStream(java.io.OutputStream, boolean) throws java.io.IOException;
field protected java.util.zip.CRC32 crc;
}
public class Inflater {
- ctor public Inflater();
ctor public Inflater(boolean);
- method public synchronized void end();
- method public synchronized boolean finished();
- method public synchronized int getAdler();
- method public synchronized long getBytesRead();
- method public synchronized long getBytesWritten();
- method public synchronized int getRemaining();
- method public synchronized int getTotalIn();
- method public synchronized int getTotalOut();
+ ctor public Inflater();
+ method public void end();
+ method public boolean finished();
+ method public int getAdler();
+ method public long getBytesRead();
+ method public long getBytesWritten();
+ method public int getRemaining();
+ method public int getTotalIn();
+ method public int getTotalOut();
+ method public int inflate(byte[], int, int) throws java.util.zip.DataFormatException;
method public int inflate(byte[]) throws java.util.zip.DataFormatException;
- method public synchronized int inflate(byte[], int, int) throws java.util.zip.DataFormatException;
- method public synchronized boolean needsDictionary();
- method public synchronized boolean needsInput();
- method public synchronized void reset();
- method public synchronized void setDictionary(byte[]);
- method public synchronized void setDictionary(byte[], int, int);
- method public synchronized void setInput(byte[]);
- method public synchronized void setInput(byte[], int, int);
+ method public boolean needsDictionary();
+ method public boolean needsInput();
+ method public void reset();
+ method public void setDictionary(byte[], int, int);
+ method public void setDictionary(byte[]);
+ method public void setInput(byte[], int, int);
+ method public void setInput(byte[]);
}
public class InflaterInputStream extends java.io.FilterInputStream {
- ctor public InflaterInputStream(java.io.InputStream);
- ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater);
ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater, int);
+ ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater);
+ ctor public InflaterInputStream(java.io.InputStream);
method protected void fill() throws java.io.IOException;
field protected byte[] buf;
+ field protected boolean closed;
field protected java.util.zip.Inflater inf;
field protected int len;
}
@@ -56857,9 +58060,12 @@
}
public class ZipFile implements java.io.Closeable {
- ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
ctor public ZipFile(java.lang.String) throws java.io.IOException;
ctor public ZipFile(java.io.File, int) throws java.io.IOException;
+ ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
+ ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
+ ctor public ZipFile(java.lang.String, java.nio.charset.Charset) throws java.io.IOException;
+ ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
method public java.lang.String getComment();
@@ -56913,6 +58119,7 @@
public class ZipInputStream extends java.util.zip.InflaterInputStream {
ctor public ZipInputStream(java.io.InputStream);
+ ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -56960,6 +58167,7 @@
public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
ctor public ZipOutputStream(java.io.OutputStream);
+ ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
method public void setComment(java.lang.String);
@@ -57019,8 +58227,8 @@
}
public class BadPaddingException extends java.security.GeneralSecurityException {
- ctor public BadPaddingException(java.lang.String);
ctor public BadPaddingException();
+ ctor public BadPaddingException(java.lang.String);
}
public class Cipher {
@@ -57131,14 +58339,14 @@
method public final int getOutputSize(int) throws java.lang.IllegalStateException;
method public final java.security.Provider getProvider();
method public final void init(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
- method public final void init(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+ method public final void init(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final boolean isCryptoAllowed(java.security.Key) throws javax.crypto.ExemptionMechanismException;
}
public class ExemptionMechanismException extends java.security.GeneralSecurityException {
- ctor public ExemptionMechanismException(java.lang.String);
ctor public ExemptionMechanismException();
+ ctor public ExemptionMechanismException(java.lang.String);
}
public abstract class ExemptionMechanismSpi {
@@ -57147,13 +58355,13 @@
method protected abstract int engineGenExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, javax.crypto.ShortBufferException;
method protected abstract int engineGetOutputSize(int);
method protected abstract void engineInit(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
- method protected abstract void engineInit(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method protected abstract void engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+ method protected abstract void engineInit(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
}
public class IllegalBlockSizeException extends java.security.GeneralSecurityException {
- ctor public IllegalBlockSizeException(java.lang.String);
ctor public IllegalBlockSizeException();
+ ctor public IllegalBlockSizeException(java.lang.String);
}
public class KeyAgreement {
@@ -57191,19 +58399,19 @@
method public static final javax.crypto.KeyGenerator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static final javax.crypto.KeyGenerator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final void init(java.security.SecureRandom);
method public final void init(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
method public final void init(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
method public final void init(int);
method public final void init(int, java.security.SecureRandom);
- method public final void init(java.security.SecureRandom);
}
public abstract class KeyGeneratorSpi {
ctor public KeyGeneratorSpi();
method protected abstract javax.crypto.SecretKey engineGenerateKey();
+ method protected abstract void engineInit(java.security.SecureRandom);
method protected abstract void engineInit(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
method protected abstract void engineInit(int, java.security.SecureRandom);
- method protected abstract void engineInit(java.security.SecureRandom);
}
public class Mac implements java.lang.Cloneable {
@@ -57218,12 +58426,12 @@
method public static final javax.crypto.Mac getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final int getMacLength();
method public final java.security.Provider getProvider();
- method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void init(java.security.Key) throws java.security.InvalidKeyException;
+ method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
method public final void reset();
method public final void update(byte) throws java.lang.IllegalStateException;
- method public final void update(byte[], int, int) throws java.lang.IllegalStateException;
method public final void update(byte[]) throws java.lang.IllegalStateException;
+ method public final void update(byte[], int, int) throws java.lang.IllegalStateException;
method public final void update(java.nio.ByteBuffer);
}
@@ -57240,8 +58448,8 @@
}
public class NoSuchPaddingException extends java.security.GeneralSecurityException {
- ctor public NoSuchPaddingException(java.lang.String);
ctor public NoSuchPaddingException();
+ ctor public NoSuchPaddingException(java.lang.String);
}
public class NullCipher extends javax.crypto.Cipher {
@@ -57282,8 +58490,8 @@
}
public class ShortBufferException extends java.security.GeneralSecurityException {
- ctor public ShortBufferException(java.lang.String);
ctor public ShortBufferException();
+ ctor public ShortBufferException(java.lang.String);
}
}
@@ -57428,7 +58636,7 @@
method public int getWordSize();
}
- public class SecretKeySpec implements java.security.spec.KeySpec javax.crypto.SecretKey java.io.Serializable {
+ public class SecretKeySpec implements java.security.spec.KeySpec javax.crypto.SecretKey {
ctor public SecretKeySpec(byte[], java.lang.String);
ctor public SecretKeySpec(byte[], int, int, java.lang.String);
method public java.lang.String getAlgorithm();
@@ -58323,7 +59531,7 @@
method public abstract java.net.ServerSocket createServerSocket(int) throws java.io.IOException;
method public abstract java.net.ServerSocket createServerSocket(int, int) throws java.io.IOException;
method public abstract java.net.ServerSocket createServerSocket(int, int, java.net.InetAddress) throws java.io.IOException;
- method public static synchronized javax.net.ServerSocketFactory getDefault();
+ method public static javax.net.ServerSocketFactory getDefault();
}
public abstract class SocketFactory {
@@ -58333,7 +59541,7 @@
method public abstract java.net.Socket createSocket(java.lang.String, int, java.net.InetAddress, int) throws java.io.IOException, java.net.UnknownHostException;
method public abstract java.net.Socket createSocket(java.net.InetAddress, int) throws java.io.IOException;
method public abstract java.net.Socket createSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
- method public static synchronized javax.net.SocketFactory getDefault();
+ method public static javax.net.SocketFactory getDefault();
}
}
@@ -58345,6 +59553,12 @@
method public java.security.cert.CertPathParameters getParameters();
}
+ public abstract class ExtendedSSLSession implements javax.net.ssl.SSLSession {
+ ctor public ExtendedSSLSession();
+ method public abstract java.lang.String[] getLocalSupportedSignatureAlgorithms();
+ method public abstract java.lang.String[] getPeerSupportedSignatureAlgorithms();
+ }
+
public class HandshakeCompletedEvent extends java.util.EventObject {
ctor public HandshakeCompletedEvent(javax.net.ssl.SSLSocket, javax.net.ssl.SSLSession);
method public java.lang.String getCipherSuite();
@@ -58420,7 +59634,7 @@
method public final javax.net.ssl.SSLEngine createSSLEngine();
method public final javax.net.ssl.SSLEngine createSSLEngine(java.lang.String, int);
method public final javax.net.ssl.SSLSessionContext getClientSessionContext();
- method public static javax.net.ssl.SSLContext getDefault() throws java.security.NoSuchAlgorithmException;
+ method public static synchronized javax.net.ssl.SSLContext getDefault() throws java.security.NoSuchAlgorithmException;
method public final javax.net.ssl.SSLParameters getDefaultSSLParameters();
method public static javax.net.ssl.SSLContext getInstance(java.lang.String) throws java.security.NoSuchAlgorithmException;
method public static javax.net.ssl.SSLContext getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
@@ -58432,13 +59646,13 @@
method public final javax.net.ssl.SSLSocketFactory getSocketFactory();
method public final javax.net.ssl.SSLParameters getSupportedSSLParameters();
method public final void init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) throws java.security.KeyManagementException;
- method public static void setDefault(javax.net.ssl.SSLContext);
+ method public static synchronized void setDefault(javax.net.ssl.SSLContext);
}
public abstract class SSLContextSpi {
ctor public SSLContextSpi();
- method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine(java.lang.String, int);
method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine();
+ method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine(java.lang.String, int);
method protected abstract javax.net.ssl.SSLSessionContext engineGetClientSessionContext();
method protected javax.net.ssl.SSLParameters engineGetDefaultSSLParameters();
method protected abstract javax.net.ssl.SSLSessionContext engineGetServerSessionContext();
@@ -58458,6 +59672,7 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
+ method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
method public abstract boolean getNeedClientAuth();
method public java.lang.String getPeerHost();
@@ -58477,12 +59692,12 @@
method public void setSSLParameters(javax.net.ssl.SSLParameters);
method public abstract void setUseClientMode(boolean);
method public abstract void setWantClientAuth(boolean);
- method public abstract javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[], int, int) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[]) throws javax.net.ssl.SSLException;
- method public abstract javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], int, int, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
- method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public abstract javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[], int, int) throws javax.net.ssl.SSLException;
method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+ method public abstract javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], int, int, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
}
public class SSLEngineResult {
@@ -58530,11 +59745,15 @@
ctor public SSLParameters();
ctor public SSLParameters(java.lang.String[]);
ctor public SSLParameters(java.lang.String[], java.lang.String[]);
+ method public java.security.AlgorithmConstraints getAlgorithmConstraints();
method public java.lang.String[] getCipherSuites();
+ method public java.lang.String getEndpointIdentificationAlgorithm();
method public boolean getNeedClientAuth();
method public java.lang.String[] getProtocols();
method public boolean getWantClientAuth();
+ method public void setAlgorithmConstraints(java.security.AlgorithmConstraints);
method public void setCipherSuites(java.lang.String[]);
+ method public void setEndpointIdentificationAlgorithm(java.lang.String);
method public void setNeedClientAuth(boolean);
method public void setProtocols(java.lang.String[]);
method public void setWantClientAuth(boolean);
@@ -58562,6 +59781,7 @@
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
method public abstract boolean getNeedClientAuth();
+ method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract java.lang.String[] getSupportedCipherSuites();
method public abstract java.lang.String[] getSupportedProtocols();
method public abstract boolean getUseClientMode();
@@ -58570,6 +59790,7 @@
method public abstract void setEnabledCipherSuites(java.lang.String[]);
method public abstract void setEnabledProtocols(java.lang.String[]);
method public abstract void setNeedClientAuth(boolean);
+ method public void setSSLParameters(javax.net.ssl.SSLParameters);
method public abstract void setUseClientMode(boolean);
method public abstract void setWantClientAuth(boolean);
}
@@ -58635,6 +59856,7 @@
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
+ method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract boolean getNeedClientAuth();
method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract javax.net.ssl.SSLSession getSession();
@@ -58690,6 +59912,14 @@
method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine);
}
+ public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager {
+ ctor public X509ExtendedTrustManager();
+ method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
+ method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+ method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
+ method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+ }
+
public abstract interface X509KeyManager implements javax.net.ssl.KeyManager {
method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket);
method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket);
@@ -58724,11 +59954,21 @@
method public abstract boolean isDestroyed();
}
+ public abstract deprecated class Policy {
+ ctor protected Policy();
+ method public abstract java.security.PermissionCollection getPermissions(javax.security.auth.Subject, java.security.CodeSource);
+ method public static javax.security.auth.Policy getPolicy();
+ method public abstract void refresh();
+ method public static void setPolicy(javax.security.auth.Policy);
+ }
+
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
+ method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
+ method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -58786,6 +60026,43 @@
package javax.security.auth.login {
+ public class AppConfigurationEntry {
+ ctor public AppConfigurationEntry(java.lang.String, javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag, java.util.Map<java.lang.String, ?>);
+ method public javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag getControlFlag();
+ method public java.lang.String getLoginModuleName();
+ method public java.util.Map<java.lang.String, ?> getOptions();
+ }
+
+ public static class AppConfigurationEntry.LoginModuleControlFlag {
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag OPTIONAL;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag REQUIRED;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag REQUISITE;
+ field public static final javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag SUFFICIENT;
+ }
+
+ public abstract class Configuration {
+ ctor protected Configuration();
+ method public abstract javax.security.auth.login.AppConfigurationEntry[] getAppConfigurationEntry(java.lang.String);
+ method public static javax.security.auth.login.Configuration getConfiguration();
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters) throws java.security.NoSuchAlgorithmException;
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+ method public static javax.security.auth.login.Configuration getInstance(java.lang.String, javax.security.auth.login.Configuration.Parameters, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+ method public javax.security.auth.login.Configuration.Parameters getParameters();
+ method public java.security.Provider getProvider();
+ method public java.lang.String getType();
+ method public void refresh();
+ method public static void setConfiguration(javax.security.auth.login.Configuration);
+ }
+
+ public static abstract interface Configuration.Parameters {
+ }
+
+ public abstract class ConfigurationSpi {
+ ctor public ConfigurationSpi();
+ method protected abstract javax.security.auth.login.AppConfigurationEntry[] engineGetAppConfigurationEntry(java.lang.String);
+ method protected void engineRefresh();
+ }
+
public class LoginException extends java.security.GeneralSecurityException {
ctor public LoginException();
ctor public LoginException(java.lang.String);
@@ -58796,10 +60073,10 @@
package javax.security.auth.x500 {
public final class X500Principal implements java.security.Principal java.io.Serializable {
- ctor public X500Principal(byte[]);
- ctor public X500Principal(java.io.InputStream);
ctor public X500Principal(java.lang.String);
ctor public X500Principal(java.lang.String, java.util.Map<java.lang.String, java.lang.String>);
+ ctor public X500Principal(byte[]);
+ ctor public X500Principal(java.io.InputStream);
method public byte[] getEncoded();
method public java.lang.String getName();
method public java.lang.String getName(java.lang.String);
@@ -58823,28 +60100,28 @@
}
public class CertificateEncodingException extends javax.security.cert.CertificateException {
- ctor public CertificateEncodingException(java.lang.String);
ctor public CertificateEncodingException();
+ ctor public CertificateEncodingException(java.lang.String);
}
public class CertificateException extends java.lang.Exception {
- ctor public CertificateException(java.lang.String);
ctor public CertificateException();
+ ctor public CertificateException(java.lang.String);
}
public class CertificateExpiredException extends javax.security.cert.CertificateException {
- ctor public CertificateExpiredException(java.lang.String);
ctor public CertificateExpiredException();
+ ctor public CertificateExpiredException(java.lang.String);
}
public class CertificateNotYetValidException extends javax.security.cert.CertificateException {
- ctor public CertificateNotYetValidException(java.lang.String);
ctor public CertificateNotYetValidException();
+ ctor public CertificateNotYetValidException(java.lang.String);
}
public class CertificateParsingException extends javax.security.cert.CertificateException {
- ctor public CertificateParsingException(java.lang.String);
ctor public CertificateParsingException();
+ ctor public CertificateParsingException(java.lang.String);
}
public abstract class X509Certificate extends javax.security.cert.Certificate {
@@ -58871,11 +60148,12 @@
public abstract interface CommonDataSource {
method public abstract java.io.PrintWriter getLogWriter() throws java.sql.SQLException;
method public abstract int getLoginTimeout() throws java.sql.SQLException;
+ method public abstract java.util.logging.Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException;
method public abstract void setLogWriter(java.io.PrintWriter) throws java.sql.SQLException;
method public abstract void setLoginTimeout(int) throws java.sql.SQLException;
}
- public class ConnectionEvent extends java.util.EventObject implements java.io.Serializable {
+ public class ConnectionEvent extends java.util.EventObject {
ctor public ConnectionEvent(javax.sql.PooledConnection);
ctor public ConnectionEvent(javax.sql.PooledConnection, java.sql.SQLException);
method public java.sql.SQLException getSQLException();
@@ -58924,21 +60202,21 @@
method public abstract void removeRowSetListener(javax.sql.RowSetListener);
method public abstract void setArray(int, java.sql.Array) throws java.sql.SQLException;
method public abstract void setAsciiStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+ method public abstract void setAsciiStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setAsciiStream(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setAsciiStream(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
- method public abstract void setAsciiStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBigDecimal(int, java.math.BigDecimal) throws java.sql.SQLException;
method public abstract void setBigDecimal(java.lang.String, java.math.BigDecimal) throws java.sql.SQLException;
method public abstract void setBinaryStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+ method public abstract void setBinaryStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBinaryStream(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBinaryStream(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
- method public abstract void setBinaryStream(java.lang.String, java.io.InputStream, int) throws java.sql.SQLException;
method public abstract void setBlob(int, java.sql.Blob) throws java.sql.SQLException;
- method public abstract void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBlob(int, java.io.InputStream, long) throws java.sql.SQLException;
- method public abstract void setBlob(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
+ method public abstract void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBlob(java.lang.String, java.io.InputStream, long) throws java.sql.SQLException;
method public abstract void setBlob(java.lang.String, java.sql.Blob) throws java.sql.SQLException;
+ method public abstract void setBlob(java.lang.String, java.io.InputStream) throws java.sql.SQLException;
method public abstract void setBoolean(int, boolean) throws java.sql.SQLException;
method public abstract void setBoolean(java.lang.String, boolean) throws java.sql.SQLException;
method public abstract void setByte(int, byte) throws java.sql.SQLException;
@@ -58946,15 +60224,15 @@
method public abstract void setBytes(int, byte[]) throws java.sql.SQLException;
method public abstract void setBytes(java.lang.String, byte[]) throws java.sql.SQLException;
method public abstract void setCharacterStream(int, java.io.Reader, int) throws java.sql.SQLException;
+ method public abstract void setCharacterStream(java.lang.String, java.io.Reader, int) throws java.sql.SQLException;
method public abstract void setCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setCharacterStream(java.lang.String, java.io.Reader, int) throws java.sql.SQLException;
method public abstract void setClob(int, java.sql.Clob) throws java.sql.SQLException;
- method public abstract void setClob(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setClob(int, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setClob(int, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setClob(java.lang.String, java.sql.Clob) throws java.sql.SQLException;
method public abstract void setClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setCommand(java.lang.String) throws java.sql.SQLException;
method public abstract void setConcurrency(int) throws java.sql.SQLException;
method public abstract void setDataSourceName(java.lang.String) throws java.sql.SQLException;
@@ -58975,26 +60253,26 @@
method public abstract void setMaxRows(int) throws java.sql.SQLException;
method public abstract void setNCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
method public abstract void setNCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
- method public abstract void setNCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
method public abstract void setNCharacterStream(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setNCharacterStream(java.lang.String, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.sql.NClob) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
+ method public abstract void setNClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
+ method public abstract void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setNClob(int, java.sql.NClob) throws java.sql.SQLException;
method public abstract void setNClob(int, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.sql.NClob) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.io.Reader) throws java.sql.SQLException;
- method public abstract void setNClob(java.lang.String, java.io.Reader, long) throws java.sql.SQLException;
method public abstract void setNString(int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNString(java.lang.String, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(int, int) throws java.sql.SQLException;
- method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int) throws java.sql.SQLException;
+ method public abstract void setNull(int, int, java.lang.String) throws java.sql.SQLException;
method public abstract void setNull(java.lang.String, int, java.lang.String) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(int, java.lang.Object, int, int) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
- method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
method public abstract void setObject(java.lang.String, java.lang.Object, int, int) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object, int) throws java.sql.SQLException;
+ method public abstract void setObject(java.lang.String, java.lang.Object) throws java.sql.SQLException;
+ method public abstract void setObject(int, java.lang.Object) throws java.sql.SQLException;
method public abstract void setPassword(java.lang.String) throws java.sql.SQLException;
method public abstract void setQueryTimeout(int) throws java.sql.SQLException;
method public abstract void setReadOnly(boolean) throws java.sql.SQLException;
@@ -59012,8 +60290,8 @@
method public abstract void setTime(java.lang.String, java.sql.Time) throws java.sql.SQLException;
method public abstract void setTime(java.lang.String, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(int, java.sql.Timestamp) throws java.sql.SQLException;
- method public abstract void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(java.lang.String, java.sql.Timestamp) throws java.sql.SQLException;
+ method public abstract void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTimestamp(java.lang.String, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
method public abstract void setTransactionIsolation(int) throws java.sql.SQLException;
method public abstract void setType(int) throws java.sql.SQLException;
@@ -59023,7 +60301,7 @@
method public abstract void setUsername(java.lang.String) throws java.sql.SQLException;
}
- public class RowSetEvent extends java.util.EventObject implements java.io.Serializable {
+ public class RowSetEvent extends java.util.EventObject {
ctor public RowSetEvent(javax.sql.RowSet);
}
@@ -59070,8 +60348,8 @@
}
public class StatementEvent extends java.util.EventObject {
- ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement, java.sql.SQLException);
ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement);
+ ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement, java.sql.SQLException);
method public java.sql.SQLException getSQLException();
method public java.sql.PreparedStatement getStatement();
}
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 7f33cb5..daf01ec 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -55,6 +55,7 @@
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -64,6 +65,7 @@
import android.view.IWindowManager;
import com.android.internal.os.BaseCommand;
+import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
@@ -72,6 +74,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
@@ -117,7 +120,8 @@
@Override
public void onShowUsage(PrintStream out) {
- out.println(
+ PrintWriter pw = new PrintWriter(out);
+ pw.println(
"usage: am [subcommand] [options]\n" +
"usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
" [--sampling INTERVAL] [-R COUNT] [-S]\n" +
@@ -149,6 +153,7 @@
" am to-app-uri [INTENT]\n" +
" am switch-user <USER_ID>\n" +
" am start-user <USER_ID>\n" +
+ " am unlock-user <USER_ID> [TOKEN_HEX]\n" +
" am stop-user [-w] <USER_ID>\n" +
" am stack start <DISPLAY_ID> <INTENT>\n" +
" am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
@@ -337,52 +342,10 @@
"am send-trim-memory: send a memory trim event to a <PROCESS>.\n" +
"\n" +
"am get-current-user: returns id of the current foreground user.\n" +
- "\n" +
- "<INTENT> specifications include these flags and arguments:\n" +
- " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
- " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
- " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
- " [--esn <EXTRA_KEY> ...]\n" +
- " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
- " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
- " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
- " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
- " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
- " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
- " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
- " (mutiple extras passed as Integer[])\n" +
- " [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
- " (mutiple extras passed as List<Integer>)\n" +
- " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
- " (mutiple extras passed as Long[])\n" +
- " [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
- " (mutiple extras passed as List<Long>)\n" +
- " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
- " (mutiple extras passed as Float[])\n" +
- " [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
- " (mutiple extras passed as List<Float>)\n" +
- " [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]\n" +
- " (mutiple extras passed as String[]; to embed a comma into a string,\n" +
- " escape it using \"\\,\")\n" +
- " [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]\n" +
- " (mutiple extras passed as List<String>; to embed a comma into a string,\n" +
- " escape it using \"\\,\")\n" +
- " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
- " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" +
- " [--debug-log-resolution] [--exclude-stopped-packages]\n" +
- " [--include-stopped-packages]\n" +
- " [--activity-brought-to-front] [--activity-clear-top]\n" +
- " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
- " [--activity-launched-from-history] [--activity-multiple-task]\n" +
- " [--activity-no-animation] [--activity-no-history]\n" +
- " [--activity-no-user-action] [--activity-previous-is-top]\n" +
- " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
- " [--activity-single-top] [--activity-clear-task]\n" +
- " [--activity-task-on-home]\n" +
- " [--receiver-registered-only] [--receiver-replace-pending]\n" +
- " [--selector]\n" +
- " [<URI> | <PACKAGE> | <COMPONENT>]\n"
- );
+ "\n"
+ );
+ Intent.printIntentArgsHelp(pw, "");
+ pw.flush();
}
@Override
@@ -450,6 +413,8 @@
runSwitchUser();
} else if (op.equals("start-user")) {
runStartUserInBackground();
+ } else if (op.equals("unlock-user")) {
+ runUnlockUser();
} else if (op.equals("stop-user")) {
runStopUser();
} else if (op.equals("stack")) {
@@ -486,10 +451,6 @@
}
private Intent makeIntent(int defUser) throws URISyntaxException {
- Intent intent = new Intent();
- Intent baseIntent = intent;
- boolean hasIntentInfo = false;
-
mStartFlags = 0;
mWaitOption = false;
mStopOption = false;
@@ -498,316 +459,38 @@
mSamplingInterval = 0;
mAutoStop = false;
mUserId = defUser;
- Uri data = null;
- String type = null;
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-a")) {
- intent.setAction(nextArgRequired());
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-d")) {
- data = Uri.parse(nextArgRequired());
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-t")) {
- type = nextArgRequired();
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-c")) {
- intent.addCategory(nextArgRequired());
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-e") || opt.equals("--es")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, value);
- } else if (opt.equals("--esn")) {
- String key = nextArgRequired();
- intent.putExtra(key, (String) null);
- } else if (opt.equals("--ei")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, Integer.decode(value));
- } else if (opt.equals("--eu")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, Uri.parse(value));
- } else if (opt.equals("--ecn")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- ComponentName cn = ComponentName.unflattenFromString(value);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + value);
- intent.putExtra(key, cn);
- } else if (opt.equals("--eia")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- int[] list = new int[strings.length];
- for (int i = 0; i < strings.length; i++) {
- list[i] = Integer.decode(strings[i]);
- }
- intent.putExtra(key, list);
- } else if (opt.equals("--eial")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- ArrayList<Integer> list = new ArrayList<>(strings.length);
- for (int i = 0; i < strings.length; i++) {
- list.add(Integer.decode(strings[i]));
- }
- intent.putExtra(key, list);
- } else if (opt.equals("--el")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, Long.valueOf(value));
- } else if (opt.equals("--ela")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- long[] list = new long[strings.length];
- for (int i = 0; i < strings.length; i++) {
- list[i] = Long.valueOf(strings[i]);
- }
- intent.putExtra(key, list);
- hasIntentInfo = true;
- } else if (opt.equals("--elal")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- ArrayList<Long> list = new ArrayList<>(strings.length);
- for (int i = 0; i < strings.length; i++) {
- list.add(Long.valueOf(strings[i]));
- }
- intent.putExtra(key, list);
- hasIntentInfo = true;
- } else if (opt.equals("--ef")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, Float.valueOf(value));
- hasIntentInfo = true;
- } else if (opt.equals("--efa")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- float[] list = new float[strings.length];
- for (int i = 0; i < strings.length; i++) {
- list[i] = Float.valueOf(strings[i]);
- }
- intent.putExtra(key, list);
- hasIntentInfo = true;
- } else if (opt.equals("--efal")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- String[] strings = value.split(",");
- ArrayList<Float> list = new ArrayList<>(strings.length);
- for (int i = 0; i < strings.length; i++) {
- list.add(Float.valueOf(strings[i]));
- }
- intent.putExtra(key, list);
- hasIntentInfo = true;
- } else if (opt.equals("--esa")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- // Split on commas unless they are preceeded by an escape.
- // The escape character must be escaped for the string and
- // again for the regex, thus four escape characters become one.
- String[] strings = value.split("(?<!\\\\),");
- intent.putExtra(key, strings);
- hasIntentInfo = true;
- } else if (opt.equals("--esal")) {
- String key = nextArgRequired();
- String value = nextArgRequired();
- // Split on commas unless they are preceeded by an escape.
- // The escape character must be escaped for the string and
- // again for the regex, thus four escape characters become one.
- String[] strings = value.split("(?<!\\\\),");
- ArrayList<String> list = new ArrayList<>(strings.length);
- for (int i = 0; i < strings.length; i++) {
- list.add(strings[i]);
- }
- intent.putExtra(key, list);
- hasIntentInfo = true;
- } else if (opt.equals("--ez")) {
- String key = nextArgRequired();
- String value = nextArgRequired().toLowerCase();
- // Boolean.valueOf() results in false for anything that is not "true", which is
- // error-prone in shell commands
- boolean arg;
- if ("true".equals(value) || "t".equals(value)) {
- arg = true;
- } else if ("false".equals(value) || "f".equals(value)) {
- arg = false;
+ return Intent.parseCommandArgs(mArgs, new Intent.CommandOptionHandler() {
+ @Override
+ public boolean handleOption(String opt, ShellCommand cmd) {
+ if (opt.equals("-D")) {
+ mStartFlags |= ActivityManager.START_FLAG_DEBUG;
+ } else if (opt.equals("-W")) {
+ mWaitOption = true;
+ } else if (opt.equals("-P")) {
+ mProfileFile = nextArgRequired();
+ mAutoStop = true;
+ } else if (opt.equals("--start-profiler")) {
+ mProfileFile = nextArgRequired();
+ mAutoStop = false;
+ } else if (opt.equals("--sampling")) {
+ mSamplingInterval = Integer.parseInt(nextArgRequired());
+ } else if (opt.equals("-R")) {
+ mRepeat = Integer.parseInt(nextArgRequired());
+ } else if (opt.equals("-S")) {
+ mStopOption = true;
+ } else if (opt.equals("--track-allocation")) {
+ mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
+ } else if (opt.equals("--user")) {
+ mUserId = parseUserArg(nextArgRequired());
+ } else if (opt.equals("--receiver-permission")) {
+ mReceiverPermission = nextArgRequired();
} else {
- try {
- arg = Integer.decode(value) != 0;
- } catch (NumberFormatException ex) {
- throw new IllegalArgumentException("Invalid boolean value: " + value);
- }
+ return false;
}
-
- intent.putExtra(key, arg);
- } else if (opt.equals("-n")) {
- String str = nextArgRequired();
- ComponentName cn = ComponentName.unflattenFromString(str);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
- intent.setComponent(cn);
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-p")) {
- String str = nextArgRequired();
- intent.setPackage(str);
- if (intent == baseIntent) {
- hasIntentInfo = true;
- }
- } else if (opt.equals("-f")) {
- String str = nextArgRequired();
- intent.setFlags(Integer.decode(str).intValue());
- } else if (opt.equals("--grant-read-uri-permission")) {
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else if (opt.equals("--grant-write-uri-permission")) {
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- } else if (opt.equals("--grant-persistable-uri-permission")) {
- intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- } else if (opt.equals("--grant-prefix-uri-permission")) {
- intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- } else if (opt.equals("--exclude-stopped-packages")) {
- intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
- } else if (opt.equals("--include-stopped-packages")) {
- intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
- } else if (opt.equals("--debug-log-resolution")) {
- intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
- } else if (opt.equals("--activity-brought-to-front")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- } else if (opt.equals("--activity-clear-top")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- } else if (opt.equals("--activity-clear-when-task-reset")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- } else if (opt.equals("--activity-exclude-from-recents")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- } else if (opt.equals("--activity-launched-from-history")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- } else if (opt.equals("--activity-multiple-task")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- } else if (opt.equals("--activity-no-animation")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- } else if (opt.equals("--activity-no-history")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
- } else if (opt.equals("--activity-no-user-action")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- } else if (opt.equals("--activity-previous-is-top")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
- } else if (opt.equals("--activity-reorder-to-front")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- } else if (opt.equals("--activity-reset-task-if-needed")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- } else if (opt.equals("--activity-single-top")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- } else if (opt.equals("--activity-clear-task")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- } else if (opt.equals("--activity-task-on-home")) {
- intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- } else if (opt.equals("--receiver-registered-only")) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- } else if (opt.equals("--receiver-replace-pending")) {
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- } else if (opt.equals("--selector")) {
- intent.setDataAndType(data, type);
- intent = new Intent();
- } else if (opt.equals("-D")) {
- mStartFlags |= ActivityManager.START_FLAG_DEBUG;
- } else if (opt.equals("-W")) {
- mWaitOption = true;
- } else if (opt.equals("-P")) {
- mProfileFile = nextArgRequired();
- mAutoStop = true;
- } else if (opt.equals("--start-profiler")) {
- mProfileFile = nextArgRequired();
- mAutoStop = false;
- } else if (opt.equals("--sampling")) {
- mSamplingInterval = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("-R")) {
- mRepeat = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("-S")) {
- mStopOption = true;
- } else if (opt.equals("--track-allocation")) {
- mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
- } else if (opt.equals("--user")) {
- mUserId = parseUserArg(nextArgRequired());
- } else if (opt.equals("--receiver-permission")) {
- mReceiverPermission = nextArgRequired();
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
+ return true;
}
- }
- intent.setDataAndType(data, type);
-
- final boolean hasSelector = intent != baseIntent;
- if (hasSelector) {
- // A selector was specified; fix up.
- baseIntent.setSelector(intent);
- intent = baseIntent;
- }
-
- String arg = nextArg();
- baseIntent = null;
- if (arg == null) {
- if (hasSelector) {
- // If a selector has been specified, and no arguments
- // have been supplied for the main Intent, then we can
- // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't
- // need to have a component name specified yet, the
- // selector will take care of that.
- baseIntent = new Intent(Intent.ACTION_MAIN);
- baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- }
- } else if (arg.indexOf(':') >= 0) {
- // The argument is a URI. Fully parse it, and use that result
- // to fill in any data not specified so far.
- baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME
- | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE);
- } else if (arg.indexOf('/') >= 0) {
- // The argument is a component name. Build an Intent to launch
- // it.
- baseIntent = new Intent(Intent.ACTION_MAIN);
- baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- baseIntent.setComponent(ComponentName.unflattenFromString(arg));
- } else {
- // Assume the argument is a package name.
- baseIntent = new Intent(Intent.ACTION_MAIN);
- baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- baseIntent.setPackage(arg);
- }
- if (baseIntent != null) {
- Bundle extras = intent.getExtras();
- intent.replaceExtras((Bundle)null);
- Bundle uriExtras = baseIntent.getExtras();
- baseIntent.replaceExtras((Bundle)null);
- if (intent.getAction() != null && baseIntent.getCategories() != null) {
- HashSet<String> cats = new HashSet<String>(baseIntent.getCategories());
- for (String c : cats) {
- baseIntent.removeCategory(c);
- }
- }
- intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
- if (extras == null) {
- extras = uriExtras;
- } else if (uriExtras != null) {
- uriExtras.putAll(extras);
- extras = uriExtras;
- }
- intent.replaceExtras(extras);
- hasIntentInfo = true;
- }
-
- if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
- return intent;
+ });
}
private void runStartService() throws Exception {
@@ -1407,6 +1090,21 @@
}
}
+ private void runUnlockUser() throws Exception {
+ int userId = Integer.parseInt(nextArgRequired());
+ String tokenHex = nextArg();
+ byte[] token = null;
+ if (tokenHex != null) {
+ token = HexDump.hexStringToByteArray(tokenHex);
+ }
+ boolean success = mAm.unlockUser(userId, token);
+ if (success) {
+ System.out.println("Success: user unlocked");
+ } else {
+ System.err.println("Error: could not unlock user");
+ }
+ }
+
private static class StopUserCallback extends IStopUserCallback.Stub {
private boolean mFinished = false;
diff --git a/cmds/appops/Android.mk b/cmds/appops/Android.mk
index 1e15204..6801ce9 100644
--- a/cmds/appops/Android.mk
+++ b/cmds/appops/Android.mk
@@ -3,14 +3,8 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := appops
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_MODULE := appops
LOCAL_SRC_FILES := appops
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
-
diff --git a/cmds/appops/appops b/cmds/appops/appops
index 407e551..25d2031 100755
--- a/cmds/appops/appops
+++ b/cmds/appops/appops
@@ -1,5 +1 @@
-# Script to start "appwidget" on the device, which has a very rudimentary shell.
-base=/system
-export CLASSPATH=$base/framework/appops.jar
-exec app_process $base/bin com.android.commands.appops.AppOpsCommand "$@"
-
+cmd appops $@
diff --git a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java
deleted file mode 100644
index c9b9e58..0000000
--- a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
-** Copyright 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.commands.appops;
-
-import android.app.ActivityManager;
-import android.app.ActivityThread;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-
-import android.util.TimeUtils;
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.os.BaseCommand;
-
-import java.io.PrintStream;
-import java.util.List;
-
-/**
- * This class is a command line utility for manipulating AppOps permissions.
- */
-public class AppOpsCommand extends BaseCommand {
-
- public static void main(String[] args) {
- new AppOpsCommand().run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println("usage: appops set [--user <USER_ID>] <PACKAGE> <OP> <MODE>\n"
- + " appops get [--user <USER_ID>] <PACKAGE> [<OP>]\n"
- + " appops reset [--user <USER_ID>] [<PACKAGE>]\n"
- + " <PACKAGE> an Android package name.\n"
- + " <OP> an AppOps operation.\n"
- + " <MODE> one of allow, ignore, deny, or default\n"
- + " <USER_ID> the user id under which the package is installed. If --user is not\n"
- + " specified, the current user is assumed.\n");
- }
-
- private static final String COMMAND_SET = "set";
- private static final String COMMAND_GET = "get";
- private static final String COMMAND_RESET = "reset";
-
- @Override
- public void onRun() throws Exception {
- String command = nextArgRequired();
- switch (command) {
- case COMMAND_SET:
- runSet();
- break;
-
- case COMMAND_GET:
- runGet();
- break;
-
- case COMMAND_RESET:
- runReset();
- break;
-
- default:
- System.err.println("Error: Unknown command: '" + command + "'.");
- break;
- }
- }
-
- private static final String ARGUMENT_USER = "--user";
-
- // Modes
- private static final String MODE_ALLOW = "allow";
- private static final String MODE_DENY = "deny";
- private static final String MODE_IGNORE = "ignore";
- private static final String MODE_DEFAULT = "default";
-
- private int strOpToOp(String op) {
- try {
- return AppOpsManager.strOpToOp(op);
- } catch (IllegalArgumentException e) {
- }
- try {
- return Integer.parseInt(op);
- } catch (NumberFormatException e) {
- }
- try {
- return AppOpsManager.strDebugOpToOp(op);
- } catch (IllegalArgumentException e) {
- System.err.println("Error: " + e.getMessage());
- return -1;
- }
- }
-
- private void runSet() throws Exception {
- String packageName = null;
- String op = null;
- String mode = null;
- int userId = UserHandle.USER_CURRENT;
- for (String argument; (argument = nextArg()) != null;) {
- if (ARGUMENT_USER.equals(argument)) {
- userId = Integer.parseInt(nextArgRequired());
- } else {
- if (packageName == null) {
- packageName = argument;
- } else if (op == null) {
- op = argument;
- } else if (mode == null) {
- mode = argument;
- } else {
- System.err.println("Error: Unsupported argument: " + argument);
- return;
- }
- }
- }
-
- if (packageName == null) {
- System.err.println("Error: Package name not specified.");
- return;
- } else if (op == null) {
- System.err.println("Error: Operation not specified.");
- return;
- } else if (mode == null) {
- System.err.println("Error: Mode not specified.");
- return;
- }
-
- final int opInt = strOpToOp(op);
- if (opInt < 0) {
- return;
- }
- final int modeInt;
- switch (mode) {
- case MODE_ALLOW:
- modeInt = AppOpsManager.MODE_ALLOWED;
- break;
- case MODE_DENY:
- modeInt = AppOpsManager.MODE_ERRORED;
- break;
- case MODE_IGNORE:
- modeInt = AppOpsManager.MODE_IGNORED;
- break;
- case MODE_DEFAULT:
- modeInt = AppOpsManager.MODE_DEFAULT;
- break;
- default:
- System.err.println("Error: Mode " + mode + " is not valid,");
- return;
- }
-
- // Parsing complete, let's execute the command.
-
- if (userId == UserHandle.USER_CURRENT) {
- userId = ActivityManager.getCurrentUser();
- }
-
- final IPackageManager pm = ActivityThread.getPackageManager();
- final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- final int uid;
- if ("root".equals(packageName)) {
- uid = 0;
- } else {
- uid = pm.getPackageUid(packageName, userId);
- }
- if (uid < 0) {
- System.err.println("Error: No UID for " + packageName + " in user " + userId);
- return;
- }
- appOpsService.setMode(opInt, uid, packageName, modeInt);
- }
-
- private void runGet() throws Exception {
- String packageName = null;
- String op = null;
- int userId = UserHandle.USER_CURRENT;
- for (String argument; (argument = nextArg()) != null;) {
- if (ARGUMENT_USER.equals(argument)) {
- userId = Integer.parseInt(nextArgRequired());
- } else {
- if (packageName == null) {
- packageName = argument;
- } else if (op == null) {
- op = argument;
- } else {
- System.err.println("Error: Unsupported argument: " + argument);
- return;
- }
- }
- }
-
- if (packageName == null) {
- System.err.println("Error: Package name not specified.");
- return;
- }
-
- final int opInt = op != null ? strOpToOp(op) : 0;
-
- // Parsing complete, let's execute the command.
-
- if (userId == UserHandle.USER_CURRENT) {
- userId = ActivityManager.getCurrentUser();
- }
-
- final IPackageManager pm = ActivityThread.getPackageManager();
- final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- final int uid;
- if ("root".equals(packageName)) {
- uid = 0;
- } else {
- uid = pm.getPackageUid(packageName, userId);
- }
- if (uid < 0) {
- System.err.println("Error: No UID for " + packageName + " in user " + userId);
- return;
- }
- List<AppOpsManager.PackageOps> ops = appOpsService.getOpsForPackage(uid, packageName,
- op != null ? new int[] {opInt} : null);
- if (ops == null || ops.size() <= 0) {
- System.out.println("No operations.");
- return;
- }
- final long now = System.currentTimeMillis();
- for (int i=0; i<ops.size(); i++) {
- List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
- for (int j=0; j<entries.size(); j++) {
- AppOpsManager.OpEntry ent = entries.get(j);
- System.out.print(AppOpsManager.opToName(ent.getOp()));
- System.out.print(": ");
- switch (ent.getMode()) {
- case AppOpsManager.MODE_ALLOWED:
- System.out.print("allow");
- break;
- case AppOpsManager.MODE_IGNORED:
- System.out.print("ignore");
- break;
- case AppOpsManager.MODE_ERRORED:
- System.out.print("deny");
- break;
- case AppOpsManager.MODE_DEFAULT:
- System.out.print("default");
- break;
- default:
- System.out.print("mode=");
- System.out.print(ent.getMode());
- break;
- }
- if (ent.getTime() != 0) {
- System.out.print("; time=");
- StringBuilder sb = new StringBuilder();
- TimeUtils.formatDuration(now - ent.getTime(), sb);
- System.out.print(sb);
- System.out.print(" ago");
- }
- if (ent.getRejectTime() != 0) {
- System.out.print("; rejectTime=");
- StringBuilder sb = new StringBuilder();
- TimeUtils.formatDuration(now - ent.getRejectTime(), sb);
- System.out.print(sb);
- System.out.print(" ago");
- }
- if (ent.getDuration() == -1) {
- System.out.print(" (running)");
- } else if (ent.getDuration() != 0) {
- System.out.print("; duration=");
- StringBuilder sb = new StringBuilder();
- TimeUtils.formatDuration(ent.getDuration(), sb);
- System.out.print(sb);
- }
- System.out.println();
- }
- }
- }
-
- private void runReset() throws Exception {
- String packageName = null;
- int userId = UserHandle.USER_CURRENT;
- for (String argument; (argument = nextArg()) != null;) {
- if (ARGUMENT_USER.equals(argument)) {
- String userStr = nextArgRequired();
- if ("all".equals(userStr)) {
- userId = UserHandle.USER_ALL;
- } else if ("current".equals(userStr)) {
- userId = UserHandle.USER_CURRENT;
- } else if ("owner".equals(userStr) || "system".equals(userStr)) {
- userId = UserHandle.USER_SYSTEM;
- } else {
- userId = Integer.parseInt(nextArgRequired());
- }
- } else {
- if (packageName == null) {
- packageName = argument;
- } else {
- System.err.println("Error: Unsupported argument: " + argument);
- return;
- }
- }
- }
-
- // Parsing complete, let's execute the command.
-
- if (userId == UserHandle.USER_CURRENT) {
- userId = ActivityManager.getCurrentUser();
- }
-
- final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
- appOpsService.resetAllModes(userId, packageName);
- System.out.print("Reset all modes for: ");
- if (userId == UserHandle.USER_ALL) {
- System.out.print("all users");
- } else {
- System.out.print("user "); System.out.print(userId);
- }
- System.out.print(", ");
- if (packageName == null) {
- System.out.println("all packages");
- } else {
- System.out.print("package "); System.out.println(packageName);
- }
- }
-}
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
index a675769..726167e 100644
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -291,7 +291,7 @@
System.err.println(" settings [--user NUM] delete namespace key");
System.err.println(" settings [--user NUM] list namespace");
System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
- System.err.println("If '--user NUM' is not given, the operations are performed on the"
+ System.err.println("If '--user NUM' is not given, the operations are performed on the "
+ "system user.");
}
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 1ee60b0..b208e43 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -86,6 +86,8 @@
runBenchmark();
} else if ("forget".equals(op)) {
runForget();
+ } else if ("set-emulate-fbe".equals(op)) {
+ runSetEmulateFbe();
} else {
throw new IllegalArgumentException();
}
@@ -137,6 +139,12 @@
StorageManager.DEBUG_FORCE_ADOPTABLE);
}
+ public void runSetEmulateFbe() throws RemoteException {
+ final boolean emulateFbe = Boolean.parseBoolean(nextArg());
+ mSm.setDebugFlags(emulateFbe ? StorageManager.DEBUG_EMULATE_FBE : 0,
+ StorageManager.DEBUG_EMULATE_FBE);
+ }
+
public void runPartition() throws RemoteException {
final String diskId = nextArg();
final String type = nextArg();
@@ -205,6 +213,8 @@
System.err.println("");
System.err.println(" sm forget [UUID|all]");
System.err.println("");
+ System.err.println(" sm set-emulate-fbe [true|false]");
+ System.err.println("");
return 1;
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9d6aa13..273483a 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -17,14 +17,19 @@
package android.accessibilityservice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Region;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -36,7 +41,10 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
/**
* An accessibility service runs in the background and receives callbacks by the system
@@ -373,6 +381,8 @@
public void init(int connectionId, IBinder windowToken);
public boolean onGesture(int gestureId);
public boolean onKeyEvent(KeyEvent event);
+ public void onMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY);
}
private int mConnectionId;
@@ -383,6 +393,8 @@
private WindowManager mWindowManager;
+ private MagnificationController mMagnificationController;
+
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
*
@@ -396,6 +408,20 @@
public abstract void onInterrupt();
/**
+ * Dispatches service connection to internal components first, then the
+ * client code.
+ */
+ private void dispatchServiceConnected() {
+ if (mMagnificationController != null) {
+ mMagnificationController.onServiceConnected();
+ }
+
+ // The client gets to handle service connection last, after we've set
+ // up any state upon which their code may rely.
+ onServiceConnected();
+ }
+
+ /**
* This method is a part of the {@link AccessibilityService} lifecycle and is
* called after the system has successfully bound to the service. If is
* convenient to use this method for setting the {@link AccessibilityServiceInfo}.
@@ -513,6 +539,385 @@
}
/**
+ * Returns the magnification controller, which may be used to query and
+ * modify the state of display magnification.
+ * <p>
+ * <strong>Note:</strong> In order to control magnification, your service
+ * must declare the capability by setting the
+ * {@link android.R.styleable#AccessibilityService_canControlMagnification}
+ * property in its meta-data. For more information, see
+ * {@link #SERVICE_META_DATA}.
+ *
+ * @return the magnification controller
+ */
+ @NonNull
+ public final MagnificationController getMagnificationController() {
+ if (mMagnificationController == null) {
+ mMagnificationController = new MagnificationController(this);
+ }
+ return mMagnificationController;
+ }
+
+ private void onMagnificationChanged(@NonNull Region region, float scale,
+ float centerX, float centerY) {
+ if (mMagnificationController != null) {
+ mMagnificationController.dispatchMagnificationChanged(
+ region, scale, centerX, centerY);
+ }
+ }
+
+ /**
+ * Used to control and query the state of display magnification.
+ */
+ public static final class MagnificationController {
+ private final AccessibilityService mService;
+
+ /**
+ * Map of listeners to their handlers. Lazily created when adding the
+ * first magnification listener.
+ */
+ private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
+
+ MagnificationController(@NonNull AccessibilityService service) {
+ mService = service;
+ }
+
+ /**
+ * Called when the service is connected.
+ */
+ void onServiceConnected() {
+ if (mListeners != null && !mListeners.isEmpty()) {
+ setMagnificationCallbackEnabled(true);
+ }
+ }
+
+ /**
+ * Adds the specified change listener to the list of magnification
+ * change listeners. The callback will occur on the service's main
+ * thread.
+ *
+ * @param listener the listener to add, must be non-{@code null}
+ */
+ public void addListener(@NonNull OnMagnificationChangedListener listener) {
+ addListener(listener, null);
+ }
+
+ /**
+ * Adds the specified change listener to the list of magnification
+ * change listeners. The callback will occur on the specified
+ * {@link Handler}'s thread, or on the service's main thread if the
+ * handler is {@code null}.
+ *
+ * @param listener the listener to add, must be non-null
+ * @param handler the handler on which the callback should execute, or
+ * {@code null} to execute on the service's main thread
+ */
+ public void addListener(@NonNull OnMagnificationChangedListener listener,
+ @Nullable Handler handler) {
+ if (mListeners == null) {
+ mListeners = new ArrayMap<>();
+ }
+
+ final boolean shouldEnableCallback = mListeners.isEmpty();
+ mListeners.put(listener, handler);
+
+ if (shouldEnableCallback) {
+ // This may fail if the service is not connected yet, but if we
+ // still have listeners when it connects then we can try again.
+ setMagnificationCallbackEnabled(true);
+ }
+ }
+
+ /**
+ * Removes all instances of the specified change listener from the list
+ * of magnification change listeners.
+ *
+ * @param listener the listener to remove, must be non-null
+ * @return {@code true} if at least one instance of the listener was
+ * removed
+ */
+ public boolean removeListener(@NonNull OnMagnificationChangedListener listener) {
+ if (mListeners == null) {
+ return false;
+ }
+
+ final int keyIndex = mListeners.indexOfKey(listener);
+ final boolean hasKey = keyIndex >= 0;
+ if (hasKey) {
+ mListeners.removeAt(keyIndex);
+ }
+
+ if (hasKey && mListeners.isEmpty()) {
+ // We just removed the last listener, so we don't need
+ // callbacks from the service anymore.
+ setMagnificationCallbackEnabled(false);
+ }
+
+ return hasKey;
+ }
+
+ private void setMagnificationCallbackEnabled(boolean enabled) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ connection.setMagnificationCallbackEnabled(enabled);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * Dispatches magnification changes to any registered listeners. This
+ * should be called on the service's main thread.
+ */
+ void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
+ final float centerX, final float centerY) {
+ if (mListeners == null || mListeners.isEmpty()) {
+ Slog.d(LOG_TAG, "Received magnification changed "
+ + "callback with no listeners registered!");
+ setMagnificationCallbackEnabled(false);
+ return;
+ }
+
+ // Listeners may remove themselves. Perform a shallow copy to avoid
+ // concurrent modification.
+ final ArrayMap<OnMagnificationChangedListener, Handler> entries =
+ new ArrayMap<>(mListeners);
+
+ for (int i = 0, count = entries.size(); i < count; i++) {
+ final OnMagnificationChangedListener listener = entries.keyAt(i);
+ final Handler handler = entries.valueAt(i);
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onMagnificationChanged(MagnificationController.this,
+ region, scale, centerX, centerY);
+ }
+ });
+ } else {
+ // We're already on the main thread, just run the listener.
+ listener.onMagnificationChanged(this, region, scale, centerX, centerY);
+ }
+ }
+ }
+
+ /**
+ * Returns the current magnification scale.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return a default value of {@code 1.0f}.
+ *
+ * @return the current magnification scale
+ */
+ public float getScale() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getMagnificationScale();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain scale", re);
+ }
+ }
+ return 1.0f;
+ }
+
+ /**
+ * Returns the unscaled screen-relative X coordinate of the focal
+ * center of the magnified region. This is the point around which
+ * zooming occurs and is guaranteed to lie within the magnified
+ * region.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return a default value of {@code 0.0f}.
+ *
+ * @return the unscaled screen-relative X coordinate of the center of
+ * the magnified region
+ */
+ public float getCenterX() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getMagnificationCenterX();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain center X", re);
+ }
+ }
+ return 0.0f;
+ }
+
+ /**
+ * Returns the unscaled screen-relative Y coordinate of the focal
+ * center of the magnified region. This is the point around which
+ * zooming occurs and is guaranteed to lie within the magnified
+ * region.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return a default value of {@code 0.0f}.
+ *
+ * @return the unscaled screen-relative Y coordinate of the center of
+ * the magnified region
+ */
+ public float getCenterY() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getMagnificationCenterY();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain center Y", re);
+ }
+ }
+ return 0.0f;
+ }
+
+ /**
+ * Returns the region of the screen currently being magnified. If
+ * magnification is not enabled, the returned region will be empty.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return an empty region.
+ *
+ * @return the screen-relative bounds of the magnified region
+ */
+ @NonNull
+ public Region getMagnifiedRegion() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getMagnifiedRegion();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain magnified region", re);
+ }
+ }
+ return Region.obtain();
+ }
+
+ /**
+ * Resets magnification scale and center to their default (e.g. no
+ * magnification) values.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will have
+ * no effect and return {@code false}.
+ *
+ * @param animate {@code true} to animate from the current scale and
+ * center or {@code false} to reset the scale and center
+ * immediately
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean reset(boolean animate) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.resetMagnification(animate);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to reset", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the magnification scale.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will have
+ * no effect and return {@code false}.
+ *
+ * @param scale the magnification scale to set, must be >= 1 and <= 5
+ * @param animate {@code true} to animate from the current scale or
+ * {@code false} to set the scale immediately
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean setScale(float scale, boolean animate) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.setMagnificationScaleAndCenter(
+ scale, Float.NaN, Float.NaN, animate);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to set scale", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the center of the magnified viewport.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will have
+ * no effect and return {@code false}.
+ *
+ * @param centerX the unscaled screen-relative X coordinate on which to
+ * center the viewport
+ * @param centerY the unscaled screen-relative Y coordinate on which to
+ * center the viewport
+ * @param animate {@code true} to animate from the current viewport
+ * center or {@code false} to set the center immediately
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean setCenter(float centerX, float centerY, boolean animate) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.setMagnificationScaleAndCenter(
+ Float.NaN, centerX, centerY, animate);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to set center", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Listener for changes in the state of magnification.
+ */
+ public interface OnMagnificationChangedListener {
+ /**
+ * Called when the magnified region, scale, or center changes.
+ *
+ * @param controller the magnification controller
+ * @param region the new magnified region, may be empty if
+ * magnification is not enabled (e.g. scale is 1)
+ * @param scale the new scale
+ * @param centerX the new X coordinate around which magnification is focused
+ * @param centerY the new Y coordinate around which magnification is focused
+ */
+ void onMagnificationChanged(@NonNull MagnificationController controller,
+ @NonNull Region region, float scale, float centerX, float centerY);
+ }
+ }
+
+ /**
* Performs a global action. Such an action can be performed
* at any moment regardless of the current application or user
* location in that application. For example going back, going
@@ -645,7 +1050,7 @@
return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
@Override
public void onServiceConnected() {
- AccessibilityService.this.onServiceConnected();
+ AccessibilityService.this.dispatchServiceConnected();
}
@Override
@@ -678,6 +1083,12 @@
public boolean onKeyEvent(KeyEvent event) {
return AccessibilityService.this.onKeyEvent(event);
}
+
+ @Override
+ public void onMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
+ }
});
}
@@ -695,6 +1106,7 @@
private static final int DO_ON_GESTURE = 4;
private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
private static final int DO_ON_KEY_EVENT = 6;
+ private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
private final HandlerCaller mCaller;
@@ -741,6 +1153,18 @@
mCaller.sendMessage(message);
}
+ public void onMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = region;
+ args.arg2 = scale;
+ args.arg3 = centerX;
+ args.arg4 = centerY;
+
+ final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
+ mCaller.sendMessage(message);
+ }
+
@Override
public void executeMessage(Message message) {
switch (message.what) {
@@ -816,6 +1240,15 @@
}
} return;
+ case DO_ON_MAGNIFICATION_CHANGED: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final Region region = (Region) args.arg1;
+ final float scale = (float) args.arg2;
+ final float centerX = (float) args.arg3;
+ final float centerY = (float) args.arg4;
+ mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+ } return;
+
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 4edb0c6..2c98fef 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -104,6 +104,12 @@
*/
public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008;
+ /**
+ * Capability: This accessibility service can control display magnification.
+ * @see android.R.styleable#AccessibilityService_canControlMagnification
+ */
+ public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010;
+
private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
new SparseArray<CapabilityInfo>();
static {
@@ -123,6 +129,10 @@
new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS,
R.string.capability_title_canRequestFilterKeyEvents,
R.string.capability_desc_canRequestFilterKeyEvents));
+ sAvailableCapabilityInfos.put(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
+ new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
+ R.string.capability_title_canControlMagnification,
+ R.string.capability_desc_canControlMagnification));
}
/**
@@ -502,6 +512,10 @@
.AccessibilityService_canRequestFilterKeyEvents, false)) {
mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
}
+ if (asAttributes.getBoolean(com.android.internal.R.styleable
+ .AccessibilityService_canControlMagnification, false)) {
+ mCapabilities |= CAPABILITY_CAN_CONTROL_MAGNIFICATION;
+ }
TypedValue peekedValue = asAttributes.peekValue(
com.android.internal.R.styleable.AccessibilityService_description);
if (peekedValue != null) {
@@ -917,6 +931,8 @@
return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS:
return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
+ case CAPABILITY_CAN_CONTROL_MAGNIFICATION:
+ return "CAPABILITY_CAN_CONTROL_MAGNIFICATION";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 8b503dd..15666bf 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -17,6 +17,7 @@
package android.accessibilityservice;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Region;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.KeyEvent;
@@ -39,4 +40,6 @@
void clearAccessibilityCache();
void onKeyEvent(in KeyEvent event, int sequence);
+
+ void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 5f7a17d..6ac50bd 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -18,6 +18,7 @@
import android.os.Bundle;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.graphics.Region;
import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
@@ -63,4 +64,19 @@
boolean performGlobalAction(int action);
oneway void setOnKeyEventResult(boolean handled, int sequence);
+
+ float getMagnificationScale();
+
+ float getMagnificationCenterX();
+
+ float getMagnificationCenterY();
+
+ Region getMagnifiedRegion();
+
+ boolean resetMagnification(boolean animate);
+
+ boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
+ boolean animate);
+
+ void setMagnificationCallbackEnabled(boolean enabled);
}
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 9c401c7f..041f591 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -116,6 +116,30 @@
*/
public static final String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
+ /**
+ * Bundle key used for the {@link String} account type in session bundle.
+ * This is used in the default implementation of
+ * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}.
+ */
+ private static final String KEY_AUTH_TOKEN_TYPE = "android.accounts.KEY_AUTH_TOKEN_TYPE";
+ /**
+ * Bundle key used for the {@link String} array of required features in
+ * session bundle. This is used in the default implementation of
+ * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}.
+ */
+ private static final String KEY_REQUIRED_FEATURES = "android.accounts.AbstractAccountAuthenticator.KEY_REQUIRED_FEATURES";
+ /**
+ * Bundle key used for the {@link Bundle} options in session bundle. This is
+ * used in default implementation of {@link #startAddAccountSession} and
+ * {@link startUpdateCredentialsSession}.
+ */
+ private static final String KEY_OPTIONS = "android.accounts.AbstractAccountAuthenticator.KEY_OPTIONS";
+ /**
+ * Bundle key used for the {@link Account} account in session bundle. This is used
+ * used in default implementation of {@link startUpdateCredentialsSession}.
+ */
+ private static final String KEY_ACCOUNT = "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT";
+
private final Context mContext;
public AbstractAccountAuthenticator(Context context) {
@@ -336,6 +360,73 @@
handleException(response, "addAccountFromCredentials", account.toString(), e);
}
}
+
+ @Override
+ public void startAddAccountSession(IAccountAuthenticatorResponse response,
+ String accountType, String authTokenType, String[] features, Bundle options)
+ throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "startAddAccountSession: accountType " + accountType
+ + ", authTokenType " + authTokenType
+ + ", features " + (features == null ? "[]" : Arrays.toString(features)));
+ }
+ checkBinderPermission();
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this.startAddAccountSession(
+ new AccountAuthenticatorResponse(response), accountType, authTokenType,
+ features, options);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (result != null) {
+ result.keySet(); // force it to be unparcelled
+ }
+ Log.v(TAG, "startAddAccountSession: result "
+ + AccountManager.sanitizeResult(result));
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (Exception e) {
+ handleException(response, "startAddAccountSession", accountType, e);
+ }
+ }
+
+ @Override
+ public void startUpdateCredentialsSession(
+ IAccountAuthenticatorResponse response,
+ Account account,
+ String authTokenType,
+ Bundle loginOptions) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "startUpdateCredentialsSession: "
+ + account
+ + ", authTokenType "
+ + authTokenType);
+ }
+ checkBinderPermission();
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this
+ .startUpdateCredentialsSession(
+ new AccountAuthenticatorResponse(response),
+ account,
+ authTokenType,
+ loginOptions);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ // Result may be null.
+ if (result != null) {
+ result.keySet(); // force it to be unparcelled
+ }
+ Log.v(TAG, "startUpdateCredentialsSession: result "
+ + AccountManager.sanitizeResult(result));
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (Exception e) {
+ handleException(response, "startUpdateCredentialsSession",
+ account.toString() + "," + authTokenType, e);
+ }
+ }
}
private void handleException(IAccountAuthenticatorResponse response, String method,
@@ -603,4 +694,97 @@
}).start();
return null;
}
+
+ /**
+ * Starts the add account session to authenticate user to an account of the
+ * specified accountType.
+ *
+ * @param response to send the result back to the AccountManager, will never
+ * be null
+ * @param accountType the type of account to authenticate with, will never
+ * be null
+ * @param authTokenType the type of auth token to retrieve after
+ * authenticating with the account, may be null
+ * @param requiredFeatures a String array of authenticator-specific features
+ * that the account authenticated with must support, may be null
+ * @param options a Bundle of authenticator-specific options, may be null
+ * @return a Bundle result or null if the result is to be returned via the
+ * response. The result will contain either:
+ * <ul>
+ * <li>{@link AccountManager#KEY_INTENT}, or
+ * <li>{@link AccountManager#KEY_ACCOUNT_SESSION_BUNDLE} for adding
+ * the account to device later, and if account is authenticated,
+ * optional {@link AccountManager#KEY_PASSWORD} and
+ * {@link AccountManager#KEY_ACCOUNT_STATUS_TOKEN} for checking the
+ * status of the account, or
+ * <li>{@link AccountManager#KEY_ERROR_CODE} and
+ * {@link AccountManager#KEY_ERROR_MESSAGE} to indicate an error
+ * </ul>
+ * @throws NetworkErrorException if the authenticator could not honor the
+ * request due to a network error
+ */
+ public Bundle startAddAccountSession(final AccountAuthenticatorResponse response,
+ final String accountType, final String authTokenType, final String[] requiredFeatures,
+ final Bundle options)
+ throws NetworkErrorException {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Bundle sessionBundle = new Bundle();
+ sessionBundle.putString(KEY_AUTH_TOKEN_TYPE, authTokenType);
+ sessionBundle.putStringArray(KEY_REQUIRED_FEATURES, requiredFeatures);
+ sessionBundle.putBundle(KEY_OPTIONS, options);
+ Bundle result = new Bundle();
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+ response.onResult(result);
+ }
+
+ }).start();
+ return null;
+ }
+
+ /**
+ * Asks user to re-authenticate for an account but defers updating the locally stored
+ * credentials.
+ *
+ * @param response to send the result back to the AccountManager, will never
+ * be null
+ * @param account the account whose credentials are to be updated, will
+ * never be null
+ * @param authTokenType the type of auth token to retrieve after updating
+ * the credentials, may be null (TODO)
+ * @param options a Bundle of authenticator-specific options, may be null
+ * @return a Bundle result or null if the result is to be returned via the
+ * response. The result will contain either:
+ * <ul>
+ * <li>{@link AccountManager#KEY_INTENT}, or
+ * <li>{@link AccountManager#KEY_ACCOUNT_SESSION_BUNDLE} for updating the
+ * locally stored credentials later, and if account is
+ * re-authenticated, {@link AccountManager#KEY_PASSWORD} and
+ * {@link AccountManager#KEY_ACCOUNT_STATUS_TOKEN} for checking the
+ * status of the account later, or
+ * <li>{@link AccountManager#KEY_ERROR_CODE} and
+ * {@link AccountManager#KEY_ERROR_MESSAGE} to indicate an error
+ * </ul>
+ * @throws NetworkErrorException if the authenticator could not honor the
+ * request due to a network error
+ */
+ public Bundle startUpdateCredentialsSession(final AccountAuthenticatorResponse response,
+ final Account account, final String authTokenType, final Bundle options)
+ throws NetworkErrorException {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Bundle sessionBundle = new Bundle();
+ sessionBundle.putString(KEY_AUTH_TOKEN_TYPE, authTokenType);
+ sessionBundle.putParcelable(KEY_ACCOUNT, account);
+ sessionBundle.putBundle(KEY_OPTIONS, options);
+ Bundle result = new Bundle();
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+ response.onResult(result);
+ }
+
+ }).start();
+ return null;
+ }
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 0a7568a..5557905 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -240,6 +240,20 @@
*/
public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
+ /**
+ * Bundle key used for a {@link Bundle} in result from
+ * {@link #startAddAccountSession} and friends which returns session data
+ * for installing an account later.
+ */
+ public static final String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+
+ /**
+ * Bundle key used for the {@link String} account status token in result
+ * from {@link #startAddAccountSession} and friends which returns
+ * information about a particular account.
+ */
+ public static final String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
+
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_META_DATA_NAME =
@@ -2590,4 +2604,170 @@
}
}
}
+
+ /**
+ * Asks the user to authenticate with an account of a specified type. The
+ * authenticator for this account type processes this request with the
+ * appropriate user interface. If the user does elect to authenticate with a
+ * new account, a bundle of session data for installing the account later is
+ * returned with optional account password and account status token.
+ * <p>
+ * This method may be called from any thread, but the returned
+ * {@link AccountManagerFuture} must not be used on the main thread.
+ * <p>
+ * <p>
+ * <b>NOTE:</b> The account will not be installed to the device by calling
+ * this api alone.
+ *
+ * @param accountType The type of account to add; must not be null
+ * @param authTokenType The type of auth token (see {@link #getAuthToken})
+ * this account will need to be able to generate, null for none
+ * @param requiredFeatures The features (see {@link #hasFeatures}) this
+ * account must have, null for none
+ * @param options Authenticator-specific options for the request, may be
+ * null or empty
+ * @param activity The {@link Activity} context to use for launching a new
+ * authenticator-defined sub-Activity to prompt the user to
+ * create an account; used only to call startActivity(); if null,
+ * the prompt will not be launched directly, but the necessary
+ * {@link Intent} will be returned to the caller instead
+ * @param callback Callback to invoke when the request completes, null for
+ * no callback
+ * @param handler {@link Handler} identifying the callback thread, null for
+ * the main thread
+ * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+ * these fields if activity was specified and user was authenticated
+ * with an account:
+ * <ul>
+ * <li>{@link #KEY_ACCOUNT_SESSION_BUNDLE} - encrypted Bundle for
+ * adding the the to the device later.
+ * <li>{@link #KEY_PASSWORD} - optional, the password or password
+ * hash of the account.
+ * <li>{@link #KEY_ACCOUNT_STATUS_TOKEN} - optional, token to check
+ * status of the account
+ * </ul>
+ * If no activity was specified, the returned Bundle contains only
+ * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+ * actual account creation process. If authenticator doesn't support
+ * this method, the returned Bundle contains only
+ * {@link #KEY_ACCOUNT_SESSION_BUNDLE} with encrypted
+ * {@code options} needed to add account later. If an error
+ * occurred, {@link AccountManagerFuture#getResult()} throws:
+ * <ul>
+ * <li>{@link AuthenticatorException} if no authenticator was
+ * registered for this account type or the authenticator failed to
+ * respond
+ * <li>{@link OperationCanceledException} if the operation was
+ * canceled for any reason, including the user canceling the
+ * creation process or adding accounts (of this type) has been
+ * disabled by policy
+ * <li>{@link IOException} if the authenticator experienced an I/O
+ * problem creating a new account, usually because of network
+ * trouble
+ * </ul>
+ */
+ public AccountManagerFuture<Bundle> startAddAccountSession(
+ final String accountType,
+ final String authTokenType,
+ final String[] requiredFeatures,
+ final Bundle options,
+ final Activity activity,
+ AccountManagerCallback<Bundle> callback,
+ Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ final Bundle optionsIn = new Bundle();
+ if (options != null) {
+ optionsIn.putAll(options);
+ }
+ optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
+ return new AmsTask(activity, handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.startAddAccountSession(
+ mResponse,
+ accountType,
+ authTokenType,
+ requiredFeatures,
+ activity != null,
+ optionsIn);
+ }
+ }.start();
+ }
+
+ /**
+ * Asks the user to enter a new password for an account but not updating the
+ * saved credentials for the account until finishSession is
+ * called.
+ * <p>
+ * This method may be called from any thread, but the returned
+ * {@link AccountManagerFuture} must not be used on the main thread.
+ * <p>
+ * <b>NOTE:</b> The saved credentials for the account alone will not be
+ * updated by calling this API alone .
+ *
+ * @param account The account to update credentials for
+ * @param authTokenType The credentials entered must allow an auth token of
+ * this type to be created (but no actual auth token is
+ * returned); may be null
+ * @param options Authenticator-specific options for the request; may be
+ * null or empty
+ * @param activity The {@link Activity} context to use for launching a new
+ * authenticator-defined sub-Activity to prompt the user to enter
+ * a password; used only to call startActivity(); if null, the
+ * prompt will not be launched directly, but the necessary
+ * {@link Intent} will be returned to the caller instead
+ * @param callback Callback to invoke when the request completes, null for
+ * no callback
+ * @param handler {@link Handler} identifying the callback thread, null for
+ * the main thread
+ * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+ * these fields if an activity was supplied and user was
+ * successfully re-authenticated to the account (TODO: default impl
+ * only returns escorw?):
+ * <ul>
+ * <li>{@link #KEY_ACCOUNT_SESSION_BUNDLE} - encrypted Bundle for
+ * updating the local credentials on device later.
+ * <li>{@link #KEY_PASSWORD} - optional, the password or password hash of the
+ * account
+ * <li>{@link #KEY_ACCOUNT_STATUS_TOKEN} - optional, token to check status of
+ * the account
+ * </ul>
+ * If no activity was specified, the returned Bundle contains
+ * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+ * password prompt. If an error occurred,
+ * {@link AccountManagerFuture#getResult()} throws:
+ * <ul>
+ * <li>{@link AuthenticatorException} if the authenticator failed to
+ * respond
+ * <li>{@link OperationCanceledException} if the operation was
+ * canceled for any reason, including the user canceling the
+ * password prompt
+ * <li>{@link IOException} if the authenticator experienced an I/O
+ * problem verifying the password, usually because of network
+ * trouble
+ * </ul>
+ */
+ public AccountManagerFuture<Bundle> startUpdateCredentialsSession(
+ final Account account,
+ final String authTokenType,
+ final Bundle options,
+ final Activity activity,
+ final AccountManagerCallback<Bundle> callback,
+ final Handler handler) {
+ if (account == null) {
+ throw new IllegalArgumentException("account is null");
+ }
+ return new AmsTask(activity, handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.startUpdateCredentialsSession(
+ mResponse,
+ account,
+ authTokenType,
+ activity != null,
+ options);
+ }
+ }.start();
+ }
}
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
index 58612da..921fb19 100644
--- a/core/java/android/accounts/IAccountAuthenticator.aidl
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -83,4 +83,17 @@
*/
void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account,
in Bundle accountCredentials);
+
+ /**
+ * Starts the add account session by prompting the user for account information
+ * and return a Bundle containing data to finish the session later.
+ */
+ void startAddAccountSession(in IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, in Bundle options);
+
+ /**
+ * Prompts the user for a new password but does not write it to the IAccountManager.
+ */
+ void startUpdateCredentialsSession(in IAccountAuthenticatorResponse response, in Account account,
+ String authTokenType, in Bundle options);
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 0d95db1..8489e47 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -83,4 +83,12 @@
String getPreviousName(in Account account);
boolean renameSharedAccountAsUser(in Account accountToRename, String newName, int userId);
+ /* Add account in two steps. */
+ void startAddAccountSession(in IAccountManagerResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+ in Bundle options);
+
+ /* Update credentials in two steps. */
+ void startUpdateCredentialsSession(in IAccountManagerResponse response, in Account account,
+ String authTokenType, boolean expectActivityLaunch, in Bundle options);
}
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index d8d2737..20d71a6 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -250,50 +250,19 @@
/**
* PathDataEvaluator is used to interpolate between two paths which are
* represented in the same format but different control points' values.
- * The path is represented as an array of PathDataNode here, which is
- * fundamentally an array of floating point numbers.
+ * The path is represented as verbs and points for each of the verbs.
*/
- private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathDataNode[]> {
- private PathParser.PathDataNode[] mNodeArray;
-
- /**
- * Create a PathParser.PathDataNode[] that does not reuse the animated value.
- * Care must be taken when using this option because on every evaluation
- * a new <code>PathParser.PathDataNode[]</code> will be allocated.
- */
- private PathDataEvaluator() {}
-
- /**
- * Create a PathDataEvaluator that reuses <code>nodeArray</code> for every evaluate() call.
- * Caution must be taken to ensure that the value returned from
- * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
- * used across threads. The value will be modified on each <code>evaluate()</code> call.
- *
- * @param nodeArray The array to modify and return from <code>evaluate</code>.
- */
- public PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
- mNodeArray = nodeArray;
- }
+ private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathData> {
+ private final PathParser.PathData mPathData = new PathParser.PathData();
@Override
- public PathParser.PathDataNode[] evaluate(float fraction,
- PathParser.PathDataNode[] startPathData,
- PathParser.PathDataNode[] endPathData) {
- if (!PathParser.canMorph(startPathData, endPathData)) {
+ public PathParser.PathData evaluate(float fraction, PathParser.PathData startPathData,
+ PathParser.PathData endPathData) {
+ if (!PathParser.interpolatePathData(mPathData, startPathData, endPathData, fraction)) {
throw new IllegalArgumentException("Can't interpolate between"
+ " two incompatible pathData");
}
-
- if (mNodeArray == null || !PathParser.canMorph(mNodeArray, startPathData)) {
- mNodeArray = PathParser.deepCopyNodes(startPathData);
- }
-
- for (int i = 0; i < startPathData.length; i++) {
- mNodeArray[i].interpolatePathDataNode(startPathData[i],
- endPathData[i], fraction);
- }
-
- return mNodeArray;
+ return mPathData;
}
}
@@ -323,13 +292,14 @@
if (valueType == VALUE_TYPE_PATH) {
String fromString = styledAttributes.getString(valueFromId);
String toString = styledAttributes.getString(valueToId);
- PathParser.PathDataNode[] nodesFrom = PathParser.createNodesFromPathData(fromString);
- PathParser.PathDataNode[] nodesTo = PathParser.createNodesFromPathData(toString);
+ PathParser.PathData nodesFrom = fromString == null
+ ? null : new PathParser.PathData(fromString);
+ PathParser.PathData nodesTo = toString == null
+ ? null : new PathParser.PathData(toString);
if (nodesFrom != null || nodesTo != null) {
if (nodesFrom != null) {
- TypeEvaluator evaluator =
- new PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
+ TypeEvaluator evaluator = new PathDataEvaluator();
if (nodesTo != null) {
if (!PathParser.canMorph(nodesFrom, nodesTo)) {
throw new InflateException(" Can't morph from " + fromString + " to " +
@@ -342,8 +312,7 @@
(Object) nodesFrom);
}
} else if (nodesTo != null) {
- TypeEvaluator evaluator =
- new PathDataEvaluator(PathParser.deepCopyNodes(nodesTo));
+ TypeEvaluator evaluator = new PathDataEvaluator();
returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
(Object) nodesTo);
}
@@ -484,23 +453,25 @@
TypeEvaluator evaluator = null;
String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
- PathParser.PathDataNode[] nodesFrom = PathParser.createNodesFromPathData(fromString);
- PathParser.PathDataNode[] nodesTo = PathParser.createNodesFromPathData(toString);
+ PathParser.PathData pathDataFrom = fromString == null
+ ? null : new PathParser.PathData(fromString);
+ PathParser.PathData pathDataTo = toString == null
+ ? null : new PathParser.PathData(toString);
- if (nodesFrom != null) {
- if (nodesTo != null) {
- anim.setObjectValues(nodesFrom, nodesTo);
- if (!PathParser.canMorph(nodesFrom, nodesTo)) {
+ if (pathDataFrom != null) {
+ if (pathDataTo != null) {
+ anim.setObjectValues(pathDataFrom, pathDataTo);
+ if (!PathParser.canMorph(pathDataFrom, pathDataTo)) {
throw new InflateException(arrayAnimator.getPositionDescription()
+ " Can't morph from " + fromString + " to " + toString);
}
} else {
- anim.setObjectValues((Object)nodesFrom);
+ anim.setObjectValues((Object)pathDataFrom);
}
- evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
- } else if (nodesTo != null) {
- anim.setObjectValues((Object)nodesTo);
- evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesTo));
+ evaluator = new PathDataEvaluator();
+ } else if (pathDataTo != null) {
+ anim.setObjectValues((Object)pathDataTo);
+ evaluator = new PathDataEvaluator();
}
if (DBG_ANIMATOR_INFLATER && evaluator != null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 472d97f..8bb0ff5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6618,6 +6618,17 @@
}
/**
+ * Set whether the caption should displayed directly on the content rather than push it down.
+ *
+ * This affects only freeform windows since they display the caption and only the main
+ * window of the activity. The caption is used to drag the window around and also shows
+ * maximize and close action buttons.
+ */
+ public void overlayWithDecorCaption(boolean overlay) {
+ mWindow.setOverlayDecorCaption(overlay);
+ }
+
+ /**
* Interface for informing a translucent {@link Activity} once all visible activities below it
* have completed drawing. This is necessary only after an {@link Activity} has been made
* opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 351064a..f7aee75 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -327,12 +327,39 @@
/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;
+ /** @hide Should this process state be considered a background state? */
+ public static final boolean isProcStateBackground(int procState) {
+ return procState >= PROCESS_STATE_BACKUP;
+ }
+
/** @hide requestType for assist context: only basic information. */
public static final int ASSIST_CONTEXT_BASIC = 0;
/** @hide requestType for assist context: generate full AssistStructure. */
public static final int ASSIST_CONTEXT_FULL = 1;
+ /** @hide Flag for registerUidObserver: report changes in process state. */
+ public static final int UID_OBSERVER_PROCSTATE = 1<<0;
+
+ /** @hide Flag for registerUidObserver: report uid gone. */
+ public static final int UID_OBSERVER_GONE = 1<<1;
+
+ /** @hide Flag for registerUidObserver: report uid has become idle. */
+ public static final int UID_OBSERVER_IDLE = 1<<2;
+
+ /** @hide Flag for registerUidObserver: report uid has become active. */
+ public static final int UID_OBSERVER_ACTIVE = 1<<3;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: normal free-to-run operation. */
+ public static final int APP_START_MODE_NORMAL = 0;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: delay running until later. */
+ public static final int APP_START_MODE_DELAYED = 1;
+
+ /** @hide Mode for {@link IActivityManager#getAppStartMode}: disable/cancel pending
+ * launches. */
+ public static final int APP_START_MODE_DISABLED = 2;
+
/**
* Lock task mode is not active.
*/
@@ -501,6 +528,15 @@
return stackId == FULLSCREEN_WORKSPACE_STACK_ID
|| stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if animation specs should be constructed for app transition that moves
+ * the task to the specified stack.
+ */
+ public static boolean useAnimationSpecForAppTransition(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID;
+ }
}
/**
@@ -857,7 +893,7 @@
if (mIcon != null) {
return mIcon;
}
- return loadTaskDescriptionIcon(mIconFilename);
+ return loadTaskDescriptionIcon(mIconFilename, UserHandle.myUserId());
}
/** @hide */
@@ -871,11 +907,11 @@
}
/** @hide */
- public static Bitmap loadTaskDescriptionIcon(String iconFilename) {
+ public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
if (iconFilename != null) {
try {
return ActivityManagerNative.getDefault().
- getTaskDescriptionIcon(iconFilename);
+ getTaskDescriptionIcon(iconFilename, userId);
} catch (RemoteException e) {
}
}
@@ -1558,6 +1594,16 @@
readFromParcel(source);
}
+ /**
+ * Resets this info state to the initial state.
+ * @hide
+ */
+ public void reset() {
+ taskWidth = 0;
+ taskHeight = 0;
+ screenOrientation = 0;
+ }
+
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
out.attribute(null, ATTR_TASK_WIDTH, Integer.toString(taskWidth));
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 65c63f6..19d9fc2 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -751,12 +751,18 @@
int taskId = data.readInt();
int createMode = data.readInt();
boolean toTop = data.readInt() != 0;
- moveTaskToDockedStack(taskId, createMode, toTop);
+ boolean animate = data.readInt() != 0;
+ Rect bounds = null;
+ boolean hasBounds = data.readInt() != 0;
+ if (hasBounds) {
+ bounds = Rect.CREATOR.createFromParcel(data);
+ }
+ moveTaskToDockedStack(taskId, createMode, toTop, animate, bounds);
reply.writeNoException();
return true;
}
- case MOVE_TOP_ACTIVITY_TO_PINNED_STACK: {
+ case MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
final Rect r = Rect.CREATOR.createFromParcel(data);
@@ -1962,6 +1968,16 @@
return true;
}
+ case UNLOCK_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int userId = data.readInt();
+ byte[] token = data.createByteArray();
+ boolean result = unlockUser(userId, token);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+
case STOP_USER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int userid = data.readInt();
@@ -2029,7 +2045,8 @@
data.enforceInterface(IActivityManager.descriptor);
IUidObserver observer = IUidObserver.Stub.asInterface(
data.readStrongBinder());
- registerUidObserver(observer);
+ int which = data.readInt();
+ registerUidObserver(observer, which);
return true;
}
@@ -2489,7 +2506,8 @@
case GET_TASK_DESCRIPTION_ICON_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String filename = data.readString();
- Bitmap icon = getTaskDescriptionIcon(filename);
+ int userId = data.readInt();
+ Bitmap icon = getTaskDescriptionIcon(filename, userId);
reply.writeNoException();
if (icon == null) {
reply.writeInt(0);
@@ -2611,6 +2629,14 @@
return true;
}
+ case UPDATE_DEVICE_OWNER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ updateDeviceOwner(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -2690,13 +2716,22 @@
reply.writeNoException();
return true;
}
- case REMOVE_STACK: {
+ case REMOVE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
removeStack(stackId);
reply.writeNoException();
return true;
}
+ case GET_APP_START_MODE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int uid = data.readInt();
+ final String pkg = data.readString();
+ int res = getAppStartMode(uid, pkg);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -3548,8 +3583,8 @@
reply.recycle();
}
@Override
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
- throws RemoteException
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -3557,6 +3592,13 @@
data.writeInt(taskId);
data.writeInt(createMode);
data.writeInt(toTop ? 1 : 0);
+ data.writeInt(animate ? 1 : 0);
+ if (initialBounds != null) {
+ data.writeInt(1);
+ initialBounds.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(MOVE_TASK_TO_DOCKED_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -3571,7 +3613,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
r.writeToParcel(data, 0);
- mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK, data, reply, 0);
+ mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION, data, reply, 0);
reply.readException();
final boolean res = reply.readInt() != 0;
data.recycle();
@@ -5231,6 +5273,20 @@
return result;
}
+ public boolean unlockUser(int userId, byte[] token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(userId);
+ data.writeByteArray(token);
+ mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean result = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -5318,11 +5374,12 @@
reply.recycle();
}
- public void registerUidObserver(IUidObserver observer) throws RemoteException {
+ public void registerUidObserver(IUidObserver observer, int which) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ data.writeInt(which);
mRemote.transact(REGISTER_UID_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -5979,11 +6036,12 @@
}
@Override
- public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
+ public Bitmap getTaskDescriptionIcon(String filename, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(filename);
+ data.writeInt(userId);
mRemote.transact(GET_TASK_DESCRIPTION_ICON_TRANSACTION, data, reply, 0);
reply.readException();
final Bitmap icon = reply.readInt() == 0 ? null : Bitmap.CREATOR.createFromParcel(reply);
@@ -6155,6 +6213,18 @@
}
@Override
+ public void updateDeviceOwner(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public int getPackageProcessState(String packageName, String callingPackage)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -6272,11 +6342,26 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
- mRemote.transact(REMOVE_STACK, data, reply, 0);
+ mRemote.transact(REMOVE_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
+ @Override
+ public int getAppStartMode(int uid, String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ data.writeString(packageName);
+ mRemote.transact(GET_APP_START_MODE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 57900aa..cee1aa5 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -64,11 +64,13 @@
public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
/**
- * The bounds that the activity should be started in. Set to null explicitly
- * for full screen. If the key is not found, previous bounds will be preserved.
+ * The bounds (window size) that the activity should be launched in. Set to null explicitly for
+ * full screen. If the key is not found, previous bounds will be preserved.
+ * NOTE: This value is ignored on devices that don't have
+ * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled.
* @hide
*/
- public static final String KEY_BOUNDS = "android:activity.bounds";
+ public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
/**
* Type of animation that arguments specify.
@@ -193,8 +195,8 @@
public static final int ANIM_CLIP_REVEAL = 11;
private String mPackageName;
- private boolean mHasBounds;
- private Rect mBounds;
+ private boolean mHasLaunchBounds;
+ private Rect mLaunchBounds;
private int mAnimationType = ANIM_NONE;
private int mCustomEnterResId;
private int mCustomExitResId;
@@ -705,9 +707,9 @@
} catch (RuntimeException e) {
Slog.w(TAG, e);
}
- mHasBounds = opts.containsKey(KEY_BOUNDS);
- if (mHasBounds) {
- mBounds = opts.getParcelable(KEY_BOUNDS);
+ mHasLaunchBounds = opts.containsKey(KEY_LAUNCH_BOUNDS);
+ if (mHasLaunchBounds) {
+ mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
}
mAnimationType = opts.getInt(KEY_ANIM_TYPE);
switch (mAnimationType) {
@@ -766,10 +768,15 @@
}
}
- /** @hide */
- public ActivityOptions setBounds(Rect bounds) {
- mHasBounds = true;
- mBounds = bounds;
+ /**
+ * Sets the bounds (window size) that the activity should be launched in. Set to null explicitly
+ * for full screen.
+ * NOTE: This value is ignored on devices that don't have
+ * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled.
+ */
+ public ActivityOptions setLaunchBounds(Rect launchBounds) {
+ mHasLaunchBounds = true;
+ mLaunchBounds = launchBounds;
return this;
}
@@ -778,14 +785,12 @@
return mPackageName;
}
- /** @hide */
- public boolean hasBounds() {
- return mHasBounds;
+ public boolean hasLaunchBounds() {
+ return mHasLaunchBounds;
}
- /** @hide */
- public Rect getBounds(){
- return mBounds;
+ public Rect getLaunchBounds(){
+ return mLaunchBounds;
}
/** @hide */
@@ -997,8 +1002,8 @@
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
- if (mHasBounds) {
- b.putParcelable(KEY_BOUNDS, mBounds);
+ if (mHasLaunchBounds) {
+ b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
}
b.putInt(KEY_ANIM_TYPE, mAnimationType);
if (mUsageTimeReport != null) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b3d6382..802880d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -78,6 +78,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.security.NetworkSecurityPolicy;
+import android.security.net.config.NetworkSecurityConfigProvider;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -4831,6 +4832,11 @@
}
}
+ // Install the Network Security Config Provider. This must happen before the application
+ // code is loaded to prevent issues with instances of TLS objects being created before
+ // the provider is installed.
+ NetworkSecurityConfigProvider.install(appContext);
+
// Continue loading instrumentation.
if (ii != null) {
final ApplicationInfo instrApp = new ApplicationInfo();
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 371c923..c075ed6 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -24,8 +24,6 @@
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.SurfaceTexture;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.OperationCanceledException;
@@ -45,6 +43,17 @@
import dalvik.system.CloseGuard;
import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.android.internal.annotations.GuardedBy;
+
/** @hide */
public class ActivityView extends ViewGroup {
@@ -53,9 +62,64 @@
private static final int MSG_SET_SURFACE = 1;
- DisplayMetrics mMetrics = new DisplayMetrics();
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int MINIMUM_POOL_SIZE = 1;
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE = 1;
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "ActivityView #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final BlockingQueue<Runnable> sPoolWorkQueue =
+ new LinkedBlockingQueue<Runnable>(128);
+
+ /**
+ * An {@link Executor} that can be used to execute tasks in parallel.
+ */
+ private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE,
+ MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+
+
+ private static class SerialExecutor implements Executor {
+ private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+ private Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ sExecutor.execute(mActive);
+ }
+ }
+ }
+
+ private final SerialExecutor mExecutor = new SerialExecutor();
+
+ private final int mDensityDpi;
private final TextureView mTextureView;
+
+ @GuardedBy("mActivityContainerLock")
private ActivityContainerWrapper mActivityContainer;
+ private Object mActivityContainerLock = new Object();
+
private Activity mActivity;
private int mWidth;
private int mHeight;
@@ -63,8 +127,6 @@
private int mLastVisibility;
private ActivityViewCallback mActivityViewCallback;
- private HandlerThread mThread = new HandlerThread("ActivityViewThread");
- private Handler mHandler;
public ActivityView(Context context) {
this(context, null);
@@ -97,28 +159,14 @@
+ e);
}
- mThread.start();
- mHandler = new Handler(mThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == MSG_SET_SURFACE) {
- try {
- mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2,
- mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- }
- }
- };
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getMetrics(mMetrics);
+ DisplayMetrics metrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(metrics);
+ mDensityDpi = metrics.densityDpi;
mLastVisibility = getVisibility();
@@ -131,15 +179,13 @@
}
@Override
- protected void onVisibilityChanged(View changedView, int visibility) {
+ protected void onVisibilityChanged(View changedView, final int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
- Message msg = Message.obtain(mHandler, MSG_SET_SURFACE);
- msg.obj = (visibility == View.GONE) ? null : mSurface;
- msg.arg1 = mWidth;
- msg.arg2 = mHeight;
- mHandler.sendMessage(msg);
+ if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable");
+ final Surface surface = (visibility == View.GONE) ? null : mSurface;
+ setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false);
}
mLastVisibility = visibility;
}
@@ -230,8 +276,10 @@
Log.e(TAG, "Duplicate call to release");
return;
}
- mActivityContainer.release();
- mActivityContainer = null;
+ synchronized (mActivityContainerLock) {
+ mActivityContainer.release();
+ mActivityContainer = null;
+ }
if (mSurface != null) {
mSurface.release();
@@ -239,25 +287,39 @@
}
mTextureView.setSurfaceTextureListener(null);
-
- mThread.quit();
}
- private void attachToSurfaceWhenReady() {
- final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
- if (surfaceTexture == null || mSurface != null) {
- // Either not ready to attach, or already attached.
- return;
- }
-
- mSurface = new Surface(surfaceTexture);
- try {
- mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- mSurface.release();
- mSurface = null;
- throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
- }
+ private void setSurfaceAsync(final Surface surface, final int width, final int height,
+ final int densityDpi, final boolean callback) {
+ mExecutor.execute(new Runnable() {
+ public void run() {
+ try {
+ synchronized (mActivityContainerLock) {
+ if (mActivityContainer != null) {
+ mActivityContainer.setSurface(surface, width, height, densityDpi);
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. ",
+ e);
+ }
+ if (callback) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityViewCallback != null) {
+ if (surface != null) {
+ mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
+ } else {
+ mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
+ }
+ }
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -308,10 +370,8 @@
+ height);
mWidth = width;
mHeight = height;
- attachToSurfaceWhenReady();
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
- }
+ mSurface = new Surface(surfaceTexture);
+ setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true);
}
@Override
@@ -331,15 +391,7 @@
if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
mSurface.release();
mSurface = null;
- try {
- mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
- }
+ setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true);
return true;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 77a9795..f0453e9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -237,8 +237,10 @@
public static final int OP_TURN_SCREEN_ON = 61;
/** @hide Get device accounts. */
public static final int OP_GET_ACCOUNTS = 62;
+ /** @hide Control whether an application is allowed to run in the background. */
+ public static final int OP_RUN_IN_BACKGROUND = 63;
/** @hide */
- public static final int _NUM_OP = 63;
+ public static final int _NUM_OP = 64;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -409,6 +411,7 @@
OP_WRITE_EXTERNAL_STORAGE,
OP_TURN_SCREEN_ON,
OP_GET_ACCOUNTS,
+ OP_RUN_IN_BACKGROUND,
};
/**
@@ -478,7 +481,8 @@
OPSTR_READ_EXTERNAL_STORAGE,
OPSTR_WRITE_EXTERNAL_STORAGE,
null,
- OPSTR_GET_ACCOUNTS
+ OPSTR_GET_ACCOUNTS,
+ null,
};
/**
@@ -549,6 +553,7 @@
"WRITE_EXTERNAL_STORAGE",
"TURN_ON_SCREEN",
"GET_ACCOUNTS",
+ "RUN_IN_BACKGROUND",
};
/**
@@ -618,7 +623,8 @@
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
null, // no permission for turning the screen on
- Manifest.permission.GET_ACCOUNTS
+ Manifest.permission.GET_ACCOUNTS,
+ null, // no permission for running in background
};
/**
@@ -690,6 +696,7 @@
null, // WRITE_EXTERNAL_STORAGE
null, // TURN_ON_SCREEN
null, // GET_ACCOUNTS
+ null, // RUN_IN_BACKGROUND
};
/**
@@ -760,6 +767,7 @@
false, // WRITE_EXTERNAL_STORAGE
false, // TURN_ON_SCREEN
false, // GET_ACCOUNTS
+ false, // RUN_IN_BACKGROUND
};
/**
@@ -829,6 +837,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED, // OP_RUN_IN_BACKGROUND
};
/**
@@ -901,7 +910,8 @@
false,
false,
false,
- false
+ false,
+ false,
};
/**
@@ -1329,7 +1339,7 @@
IAppOpsCallback cb = mModeWatchers.get(callback);
if (cb == null) {
cb = new IAppOpsCallback.Stub() {
- public void opChanged(int op, String packageName) {
+ public void opChanged(int op, int uid, String packageName) {
if (callback instanceof OnOpChangedInternalListener) {
((OnOpChangedInternalListener)callback).onOpChanged(op, packageName);
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 0745537..20f3495 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -248,6 +248,7 @@
} else if (mTransitioningViews != null) {
mTransitioningViews.addAll(mSharedElements);
}
+ moveSharedElementsFromOverlay();
mSharedElementNames.clear();
mSharedElements.clear();
mAllSharedElementNames.clear();
@@ -574,14 +575,20 @@
setGhostVisibility(View.INVISIBLE);
mHasStopped = true;
mIsCanceled = true;
+ clearState();
+ return super.cancelPendingTransitions();
+ }
+
+ @Override
+ protected void clearState() {
+ mSharedElementsBundle = null;
+ mEnterViewsTransition = null;
mResultReceiver = null;
if (mBackgroundAnimator != null) {
mBackgroundAnimator.cancel();
mBackgroundAnimator = null;
}
- mActivity = null;
- clearState();
- return super.cancelPendingTransitions();
+ super.clearState();
}
private void makeOpaque() {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 4b670cd..e93b40e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -470,6 +470,11 @@
mActivity = null;
}
// Clear the state so that we can't hold any references accidentally and leak memory.
+ clearState();
+ }
+
+ @Override
+ protected void clearState() {
mHandler = null;
mSharedElementBundle = null;
if (mBackgroundAnimator != null) {
@@ -477,7 +482,7 @@
mBackgroundAnimator = null;
}
mExitSharedElementBundle = null;
- clearState();
+ super.clearState();
}
@Override
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cf2452b..09c6c0b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -142,8 +142,8 @@
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
- throws RemoteException;
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) throws RemoteException;
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
@@ -390,6 +390,7 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
public boolean startUserInBackground(int userid) throws RemoteException;
+ public boolean unlockUser(int userid, byte[] token) throws RemoteException;
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean isUserRunning(int userid, int flags) throws RemoteException;
@@ -400,7 +401,7 @@
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
- public void registerUidObserver(IUidObserver observer) throws RemoteException;
+ public void registerUidObserver(IUidObserver observer, int which) throws RemoteException;
public void unregisterUidObserver(IUidObserver observer) throws RemoteException;
public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
@@ -497,7 +498,7 @@
public void resizeTask(int taskId, Rect bounds, int resizeMode) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
- public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
+ public Bitmap getTaskDescriptionIcon(String filename, int userId) throws RemoteException;
public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)
throws RemoteException;
@@ -518,6 +519,7 @@
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public void updateDeviceOwner(String packageName) throws RemoteException;
public int getPackageProcessState(String packageName, String callingPackage)
throws RemoteException;
@@ -541,6 +543,8 @@
public void removeStack(int stackId) throws RemoteException;
+ public int getAppStartMode(int uid, String packageName) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -880,6 +884,7 @@
int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
+ int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
@@ -897,6 +902,8 @@
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
- int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
- int MOVE_TOP_ACTIVITY_TO_PINNED_STACK = IBinder.FIRST_CALL_TRANSACTION + 349;
+ int REMOVE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
+ int MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 349;
+ int GET_APP_START_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 350;
+ int UNLOCK_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 351;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 30232da..c1d5b19 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -47,14 +47,11 @@
void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
boolean areNotificationsEnabledForPackage(String pkg, int uid);
- void setPackagePriority(String pkg, int uid, int priority);
- int getPackagePriority(String pkg, int uid);
-
- void setPackagePeekable(String pkg, int uid, boolean peekable);
- boolean getPackagePeekable(String pkg, int uid);
-
- void setPackageVisibilityOverride(String pkg, int uid, int visibility);
- int getPackageVisibilityOverride(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);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 308cb94..fa8d0c9 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -18,6 +18,24 @@
/** {@hide} */
oneway interface IUidObserver {
+ /**
+ * General report of a state change of an uid.
+ */
void onUidStateChanged(int uid, int procState);
+
+ /**
+ * Report that there are no longer any processes running for a uid.
+ */
void onUidGone(int uid);
+
+ /**
+ * Report that a uid is now active (no longer idle).
+ */
+ void onUidActive(int uid);
+
+ /**
+ * Report that a uid is idle -- it has either been running in the background for
+ * a sufficient period of time, or all of its processes have gone away.
+ */
+ void onUidIdle(int uid);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8b7abb7..891558f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -275,7 +275,7 @@
if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
final String isa = VMRuntime.getRuntime().vmInstructionSet();
try {
- ActivityThread.getPackageManager().performDexOptIfNeeded(mPackageName, isa);
+ ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
} catch (RemoteException re) {
// Ignored.
}
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..3f1d113 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
package android.app;
parcelable Notification;
+parcelable Notification.Topic;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0b77be3..74634a9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -838,13 +839,6 @@
public static final String EXTRA_PEOPLE = "android.people";
/**
- * {@link #extras} key: used to provide hints about the appropriateness of
- * displaying this notification as a heads-up notification.
- * @hide
- */
- public static final String EXTRA_AS_HEADS_UP = "headsup";
-
- /**
* Allow certain system-generated notifications to appear before the device is provisioned.
* Only available to notifications coming from the android package.
* @hide
@@ -887,32 +881,6 @@
*/
public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
- /**
- * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
- * displayed in the heads up space.
- *
- * <p>
- * If this notification has a {@link #fullScreenIntent}, then it will always launch the
- * full-screen intent when posted.
- * </p>
- * @hide
- */
- public static final int HEADS_UP_NEVER = 0;
-
- /**
- * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
- * displayed as a heads up.
- * @hide
- */
- public static final int HEADS_UP_ALLOWED = 1;
-
- /**
- * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
- * good candidate for display as a heads up.
- * @hide
- */
- public static final int HEADS_UP_REQUESTED = 2;
-
private Icon mSmallIcon;
private Icon mLargeIcon;
@@ -1466,10 +1434,13 @@
};
}
- private Topic[] topics;
+ @SystemApi
+ public static final String TOPIC_DEFAULT = "system_default_topic";
- public Topic[] getTopics() {
- return topics;
+ private Topic topic;
+
+ public Topic getTopic() {
+ return topic;
}
/**
@@ -1599,7 +1570,9 @@
color = parcel.readInt();
- topics = parcel.createTypedArray(Topic.CREATOR); // may be null
+ if (parcel.readInt() != 0) {
+ topic = Topic.CREATOR.createFromParcel(parcel);
+ }
}
@Override
@@ -1700,11 +1673,8 @@
that.color = this.color;
- if (this.topics != null) {
- that.topics = new Topic[this.topics.length];
- for(int i=0; i<this.topics.length; i++) {
- that.topics[i] = this.topics[i].clone();
- }
+ if (this.topic != null) {
+ that.topic = this.topic.clone();
}
if (!heavy) {
@@ -1878,7 +1848,12 @@
parcel.writeInt(color);
- parcel.writeTypedArray(topics, 0); // null ok
+ if (topic != null) {
+ parcel.writeInt(1);
+ topic.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
}
/**
@@ -1940,6 +1915,19 @@
builder.build(); // callers expect this notification to be ready to use
}
+ /**
+ * @hide
+ */
+ public static void addFieldsFromContext(Context context, Notification notification) {
+ if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
+ notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
+ context.getApplicationInfo());
+ }
+ if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
+ notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -2008,17 +1996,9 @@
sb.append(" publicVersion=");
sb.append(publicVersion.toString());
}
- if (topics != null) {
- sb.append("topics=[");
- int N = topics.length;
- if (N > 0) {
- for (int i = 0; i < N-1; i++) {
- sb.append(topics[i]);
- sb.append(',');
- }
- sb.append(topics[N-1]);
- }
- sb.append("]");
+ if (topic != null) {
+ sb.append("topic=");
+ sb.append(topic.toString());
}
sb.append(")");
return sb.toString();
@@ -2136,12 +2116,6 @@
private ArrayList<String> mPersonList = new ArrayList<String>();
private NotificationColorUtil mColorUtil;
private boolean mColorUtilInited = false;
- private List<Topic> mTopics = new ArrayList<>();
-
- /**
- * The user that built the notification originally.
- */
- private int mOriginatingUserId;
/**
* Constructs a new Builder with the defaults:
@@ -2187,10 +2161,6 @@
Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
}
- if (mN.getTopics() != null) {
- Collections.addAll(mTopics, mN.getTopics());
- }
-
String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
if (!TextUtils.isEmpty(templateClass)) {
final Class<? extends Style> styleClass
@@ -2962,15 +2932,15 @@
}
/**
- * Add a topic to this notification. Topics are typically displayed in Notification
+ * Sets the topic of this notification. Topics are typically displayed in Notification
* settings.
* <p>
* Every topic must have an id and a textual label.
*
* @param topic The topic to add.
*/
- public Builder addTopic(Topic topic) {
- mTopics.add(topic);
+ public Builder setTopic(Topic topic) {
+ mN.topic = topic;
return this;
}
@@ -2978,7 +2948,7 @@
// Note: This assumes that the current user can read the profile badge of the
// originating user.
return mContext.getPackageManager().getUserBadgeForDensity(
- new UserHandle(mOriginatingUserId), 0);
+ new UserHandle(mContext.getUserId()), 0);
}
private Bitmap getProfileBadge() {
@@ -3280,12 +3250,16 @@
* Construct a RemoteViews for the final big notification layout.
*/
public RemoteViews makeBigContentView() {
- if (mStyle != null) {
+ if (mN.bigContentView != null) {
+ return mN.bigContentView;
+ } else if (mStyle != null) {
final RemoteViews styleView = mStyle.makeBigContentView();
if (styleView != null) {
return styleView;
}
- } else if (mActions.size() == 0) return null;
+ } else if (mActions.size() == 0) {
+ return null;
+ }
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
@@ -3294,12 +3268,17 @@
* Construct a RemoteViews for the final heads-up notification layout.
*/
public RemoteViews makeHeadsUpContentView() {
- if (mStyle != null) {
- final RemoteViews styleView = mStyle.makeHeadsUpContentView();
- if (styleView != null) {
- return styleView;
- }
- } else if (mActions.size() == 0) return null;
+ if (mN.headsUpContentView != null) {
+ return mN.headsUpContentView;
+ } else if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeHeadsUpContentView();
+ if (styleView != null) {
+ return styleView;
+ }
+ } else if (mActions.size() == 0) {
+ return null;
+ }
+
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
@@ -3311,12 +3290,17 @@
tombstone ? getActionTombstoneLayoutResource()
: getActionLayoutResource());
final Icon ai = action.getIcon();
- button.setTextViewCompoundDrawablesRelative(R.id.action0, ai, null, null, null);
button.setTextViewText(R.id.action0, processLegacyText(action.title));
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
}
button.setContentDescription(R.id.action0, action.title);
+ if (action.mRemoteInputs != null) {
+ button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
+ }
+ if (mN.color != COLOR_DEFAULT) {
+ button.setTextColor(R.id.action0, mN.color);
+ }
processLegacyAction(action, button);
return button;
}
@@ -3452,10 +3436,6 @@
mN.extras.putStringArray(EXTRA_PEOPLE,
mPersonList.toArray(new String[mPersonList.size()]));
}
- if (mTopics.size() > 0) {
- mN.topics = new Topic[mTopics.size()];
- mTopics.toArray(mN.topics);
- }
return mN;
}
@@ -3464,12 +3444,16 @@
ApplicationInfo applicationInfo = n.extras.getParcelable(
EXTRA_BUILDER_APPLICATION_INFO);
Context builderContext;
- try {
- builderContext = context.createApplicationContext(applicationInfo,
- Context.CONTEXT_RESTRICTED);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
- builderContext = context; // try with our context
+ if (applicationInfo != null) {
+ try {
+ builderContext = context.createApplicationContext(applicationInfo,
+ Context.CONTEXT_RESTRICTED);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
+ builderContext = context; // try with our context
+ }
+ } else {
+ builderContext = context; // try with given context
}
return new Builder(builderContext, n);
@@ -3518,9 +3502,7 @@
}
// lazy stuff from mContext; see comment in Builder(Context, Notification)
- mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
- mOriginatingUserId = mContext.getUserId();
- mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
+ Notification.addFieldsFromContext(mContext, mN);
buildUnstyled();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 07b4d39..3eb3e0f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -218,6 +218,8 @@
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
+ // Fix the notification as best we can.
+ Notification.addFieldsFromContext(mContext, notification);
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
@@ -610,15 +612,39 @@
* PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */
public final int priorityMessageSenders;
+ public static final int SUPPRESSED_EFFECTS_UNSET = -1;
+ public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 0;
+ public static final int SUPPRESSED_EFFECT_PEEK = 1 << 1;
+
+ private static final int[] ALL_SUPPRESSED_EFFECTS = {
+ SUPPRESSED_EFFECT_LIGHTS,
+ SUPPRESSED_EFFECT_PEEK,
+ };
+
+ /**
+ * Visual effects to suppress for a notification that is filtered by Do Not Disturb mode.
+ * Bitmask of SUPPRESSED_EFFECT_* constants.
+ */
+ public final int suppressedVisualEffects;
+
+
+ @Deprecated
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
+ this(priorityCategories, priorityCallSenders, priorityMessageSenders,
+ SUPPRESSED_EFFECTS_UNSET);
+ }
+
+ public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
+ int suppressedVisualEffects) {
this.priorityCategories = priorityCategories;
this.priorityCallSenders = priorityCallSenders;
this.priorityMessageSenders = priorityMessageSenders;
+ this.suppressedVisualEffects = suppressedVisualEffects;
}
/** @hide */
public Policy(Parcel source) {
- this(source.readInt(), source.readInt(), source.readInt());
+ this(source.readInt(), source.readInt(), source.readInt(), source.readInt());
}
@Override
@@ -626,6 +652,7 @@
dest.writeInt(priorityCategories);
dest.writeInt(priorityCallSenders);
dest.writeInt(priorityMessageSenders);
+ dest.writeInt(suppressedVisualEffects);
}
@Override
@@ -635,7 +662,8 @@
@Override
public int hashCode() {
- return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders);
+ return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders,
+ suppressedVisualEffects);
}
@Override
@@ -645,7 +673,8 @@
final Policy other = (Policy) o;
return other.priorityCategories == priorityCategories
&& other.priorityCallSenders == priorityCallSenders
- && other.priorityMessageSenders == priorityMessageSenders;
+ && other.priorityMessageSenders == priorityMessageSenders
+ && other.suppressedVisualEffects == suppressedVisualEffects;
}
@Override
@@ -654,9 +683,29 @@
+ "priorityCategories=" + priorityCategoriesToString(priorityCategories)
+ ",priorityCallSenders=" + prioritySendersToString(priorityCallSenders)
+ ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders)
+ + ",suppressedVisualEffects="
+ + suppressedEffectsToString(suppressedVisualEffects)
+ "]";
}
+ public static String suppressedEffectsToString(int effects) {
+ if (effects <= 0) return "";
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) {
+ final int effect = ALL_SUPPRESSED_EFFECTS[i];
+ if ((effects & effect) != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append(effectToString(effect));
+ }
+ effects &= ~effect;
+ }
+ if (effects != 0) {
+ if (sb.length() > 0) sb.append(',');
+ sb.append("UNKNOWN_").append(effects);
+ }
+ return sb.toString();
+ }
+
public static String priorityCategoriesToString(int priorityCategories) {
if (priorityCategories == 0) return "";
final StringBuilder sb = new StringBuilder();
@@ -675,6 +724,15 @@
return sb.toString();
}
+ private static String effectToString(int effect) {
+ switch (effect) {
+ case SUPPRESSED_EFFECT_LIGHTS: return "SUPPRESSED_EFFECT_LIGHTS";
+ case SUPPRESSED_EFFECT_PEEK: return "SUPPRESSED_EFFECT_PEEK";
+ case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET";
+ default: return "UNKNOWN_" + effect;
+ }
+ }
+
private static String priorityCategoryToString(int priorityCategory) {
switch (priorityCategory) {
case PRIORITY_CATEGORY_REMINDERS: return "PRIORITY_CATEGORY_REMINDERS";
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index a3b3022..aca0763 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -23,10 +23,8 @@
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;
-import android.widget.TimePicker.ValidationCallback;
import com.android.internal.R;
@@ -64,7 +62,7 @@
* @param hourOfDay the hour that was set
* @param minute the minute that was set
*/
- public void onTimeSet(TimePicker view, int hourOfDay, int minute);
+ void onTimeSet(TimePicker view, int hourOfDay, int minute);
}
/**
@@ -115,7 +113,6 @@
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.timePickerDialogTheme, outValue, true);
- final int layoutResId = outValue.resourceId;
final LayoutInflater inflater = LayoutInflater.from(themeContext);
final View view = inflater.inflate(R.layout.time_picker_dialog, null);
@@ -129,7 +126,6 @@
mTimePicker.setCurrentHour(mInitialHourOfDay);
mTimePicker.setCurrentMinute(mInitialMinute);
mTimePicker.setOnTimeChangedListener(this);
- mTimePicker.setValidationCallback(mValidationCallback);
}
@Override
@@ -181,14 +177,4 @@
mTimePicker.setCurrentHour(hour);
mTimePicker.setCurrentMinute(minute);
}
-
- private final ValidationCallback mValidationCallback = new ValidationCallback() {
- @Override
- public void onValidationChanged(boolean valid) {
- final Button positive = getButton(BUTTON_POSITIVE);
- if (positive != null) {
- positive.setEnabled(valid);
- }
- }
- };
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index efed2e0..f7848f9 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -21,9 +21,11 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.annotation.NonNull;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
+import android.graphics.Region;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.os.Looper;
@@ -1020,6 +1022,12 @@
public boolean onKeyEvent(KeyEvent event) {
return false;
}
+
+ @Override
+ public void onMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ /* do nothing */
+ }
});
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 66defa6..471750e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -31,6 +31,7 @@
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.ProxyInfo;
+import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.Process;
@@ -40,6 +41,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.provider.ContactsContract.Directory;
import android.security.Credentials;
import android.service.restrictions.RestrictionsReceiver;
import android.util.Log;
@@ -87,6 +89,10 @@
private final Context mContext;
private final IDevicePolicyManager mService;
+ // TODO Use it everywhere.
+ private static final String REMOTE_EXCEPTION_MESSAGE =
+ "Failed to talk with device policy manager service";
+
private DevicePolicyManager(Context context) {
this(context, IDevicePolicyManager.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
@@ -122,12 +128,21 @@
* Provisioning adds a managed profile and sets the MDM as the profile owner who has full
* control over the profile.
*
- * In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this intent must contain the
+ * <p>It is possible to check if provisioning is allowed or not by querying the method
+ * {@link #isProvisioningAllowed(String)}.
+ *
+ * <p>In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this intent must contain the
* extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}.
* As of {@link android.os.Build.VERSION_CODES#M}, it should contain the extra
* {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, although specifying only
* {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported.
*
+ * <p> The intent may also contain the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_PROVISIONING_LOGO_URI}, optional </li>
+ * <li> {@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional </li>
+ * </ul>
+ *
* <p> When managed provisioning has completed, broadcasts are sent to the application specified
* in the provisioning intent. The
* {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} broadcast is sent in the
@@ -149,7 +164,24 @@
/**
* @hide
- * TODO Add Documentation
+ * Activity action: Starts the provisioning flow which sets up a managed user.
+ *
+ * <p>This intent will typically be sent by a mobile device management application (MDM).
+ * Provisioning configures the current user as managed user and sets the MDM as the profile
+ * owner who has full control over the user. Provisioning can only happen before user setup has
+ * been completed. Use {@link #isProvisioningAllowed(String)} to check if provisioning is
+ * allowed.
+ *
+ * <p>This intent should contain the extra
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
+ *
+ * <p> If provisioning fails, the device returns to its previous state.
+ *
+ * <p>If launched with {@link android.app.Activity#startActivityForResult(Intent, int)} a
+ * result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part of
+ * the provisioning flow was successful, although this doesn't guarantee the full flow will
+ * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
+ * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROVISION_MANAGED_USER
@@ -167,10 +199,57 @@
* employee or client.
*
* <p> An intent with this action can be sent only on an unprovisioned device.
- * It is possible to check if the device is provisioned or not by looking at
- * {@link android.provider.Settings.Global#DEVICE_PROVISIONED}
+ * It is possible to check if provisioning is allowed or not by querying the method
+ * {@link #isProvisioningAllowed(String)}.
*
- * The intent contains the following extras:
+ * <p>The intent contains the following extras:
+ * <ul>
+ * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
+ * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
+ * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
+ * </ul>
+ *
+ * <p> When device owner provisioning has completed, an intent of the type
+ * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcast to the
+ * device owner.
+ *
+ * <p> If provisioning fails, the device is factory reset.
+ *
+ * <p>A result code of {@link android.app.Activity#RESULT_OK} implies that the synchronous part
+ * of the provisioning flow was successful, although this doesn't guarantee the full flow will
+ * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
+ * that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROVISION_MANAGED_DEVICE
+ = "android.app.action.PROVISION_MANAGED_DEVICE";
+
+ /**
+ * Activity action: Starts the provisioning flow which sets up a managed device.
+ * Must be started with {@link android.app.Activity#startActivityForResult(Intent, int)}.
+ *
+ * <p>NOTE: This is only supported on split system user devices, and puts the device into a
+ * management state that is distinct from that reached by
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE} - specifically the device owner runs on the system
+ * user, and only has control over device-wide policies, not individual users and their data.
+ * The primary benefit is that multiple non-system users are supported when provisioning using
+ * this form of device management.
+ *
+ * <p> During device owner provisioning a device admin app is set as the owner of the device.
+ * A device owner has full control over the device. The device owner can not be modified by the
+ * user.
+ *
+ * <p> A typical use case would be a device that is owned by a company, but used by either an
+ * employee or client.
+ *
+ * <p> An intent with this action can be sent only on an unprovisioned device.
+ * It is possible to check if provisioning is allowed or not by querying the method
+ * {@link #isProvisioningAllowed(String)}.
+ *
+ * <p>The intent contains the following extras:
* <ul>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li>
* <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
@@ -188,10 +267,12 @@
* of the provisioning flow was successful, although this doesn't guarantee the full flow will
* succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies
* that the user backed-out of provisioning, or some precondition for provisioning wasn't met.
+ *
+ * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PROVISION_MANAGED_DEVICE
- = "android.app.action.PROVISION_MANAGED_DEVICE";
+ public static final String ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE
+ = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
/**
* A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
@@ -288,6 +369,16 @@
= "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
/**
+ * A integer extra indicating the predominant color to show during the provisioning.
+ * Refer to {@link android.graphics.Color} for how the color is represented.
+ *
+ * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}.
+ */
+ public static final String EXTRA_PROVISIONING_MAIN_COLOR =
+ "android.app.extra.PROVISIONING_MAIN_COLOR";
+
+ /**
* A Boolean extra that can be used by the mobile device management application to skip the
* disabling of system apps during provisioning when set to {@code true}.
*
@@ -506,6 +597,28 @@
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
/**
+ * A {@link Uri} extra pointing to a logo image. This image will be shown during the
+ * provisioning. If this extra is not passed, a default image will be shown.
+ * <h5>The following URI schemes are accepted:</h5>
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
+ * </ul>
+ *
+ * <p> It is the responsability of the caller to provide an image with a reasonable
+ * pixed density for the device.
+ *
+ * <p> If a content: URI is passed, the intent should have the flag
+ * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
+ * {@link android.content.ClipData} of the intent too.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}
+ */
+ public static final String EXTRA_PROVISIONING_LOGO_URI =
+ "android.app.extra.PROVISIONING_LOGO_URI";
+
+ /**
* This MIME type is used for starting the Device Owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -1644,7 +1757,16 @@
* Force a new device unlock password (the password needed to access the
* entire device, not for individual accounts) on the user. This takes
* effect immediately.
- * The given password must be sufficient for the
+ *
+ * <p>Calling this from a managed profile that shares the password with the owner profile
+ * will throw a security exception.
+ *
+ * <p><em>Note: This API has been limited as of {@link android.os.Build.VERSION_CODES#N} for
+ * device admins that are not device owner and not profile owner.
+ * The password can now only be changed if there is currently no password set. Device owner
+ * and profile owner can still do this.</em>
+ *
+ * <p>The given password must be sufficient for the
* current password quality and length constraints as returned by
* {@link #getPasswordQuality(ComponentName)} and
* {@link #getPasswordMinimumLength(ComponentName)}; if it does not meet
@@ -1654,19 +1776,20 @@
* the currently active quality will be increased to match.
*
* <p>Calling with a null or empty password will clear any existing PIN,
- * pattern or password if the current password constraints allow it.
+ * pattern or password if the current password constraints allow it. <em>Note: This will not
+ * work in {@link android.os.Build.VERSION_CODES#N} and later for device admins that are not
+ * device owner and not profile owner. Once set, the password cannot be changed to null or
+ * empty, except by device owner or profile owner.</em>
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
* this method; if it has not, a security exception will be thrown.
*
- * <p>Calling this from a managed profile will throw a security exception.
- *
* @param password The new password for the user. Null or empty clears the password.
* @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and
* {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}.
* @return Returns true if the password was applied, or false if it is
- * not acceptable for the current constraints.
+ * not acceptable for the current constraints or if the user has not been decrypted yet.
*/
public boolean resetPassword(String password, int flags) {
if (mService != null) {
@@ -1774,7 +1897,7 @@
public void wipeData(int flags) {
if (mService != null) {
try {
- mService.wipeData(flags, myUserId());
+ mService.wipeData(flags);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2646,37 +2769,94 @@
* the setup process.
* @param packageName the package name of the app, to compare with the registered device owner
* app, if any.
- * @return whether or not the package is registered as the device owner app. Note this method
- * does *not* check weather the device owner is actually running on the current user.
+ * @return whether or not the package is registered as the device owner app.
*/
public boolean isDeviceOwnerApp(String packageName) {
- if (mService != null) {
- try {
- return mService.isDeviceOwnerPackage(packageName);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed talking with device policy service", e);
- }
- }
- return false;
+ return isDeviceOwnerAppOnCallingUser(packageName);
}
/**
+ * @return true if a package is registered as device owner, only when it's running on the
+ * calling user.
+ *
+ * <p>Same as {@link #isDeviceOwnerApp}, but bundled code should use it for clarity.
* @hide
- * Redirect to isDeviceOwnerApp.
*/
- public boolean isDeviceOwner(String packageName) {
- return isDeviceOwnerApp(packageName);
+ public boolean isDeviceOwnerAppOnCallingUser(String packageName) {
+ return isDeviceOwnerAppOnAnyUserInner(packageName, /* callingUserOnly =*/ true);
}
/**
- * Check whether a given component is registered as a device owner.
- * Note this method does *not* check weather the device owner is actually running on the current
+ * @return true if a package is registered as device owner, even if it's running on a different
* user.
*
+ * <p>Requires the MANAGE_USERS permission.
+ *
* @hide
*/
- public boolean isDeviceOwner(ComponentName who) {
- return (who != null) && who.equals(getDeviceOwner());
+ public boolean isDeviceOwnerAppOnAnyUser(String packageName) {
+ return isDeviceOwnerAppOnAnyUserInner(packageName, /* callingUserOnly =*/ false);
+ }
+
+ /**
+ * @return device owner component name, only when it's running on the calling user.
+ *
+ * @hide
+ */
+ public ComponentName getDeviceOwnerComponentOnCallingUser() {
+ return getDeviceOwnerComponentInner(/* callingUserOnly =*/ true);
+ }
+
+ /**
+ * @return device owner component name, even if it's running on a different user.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false);
+ }
+
+ private boolean isDeviceOwnerAppOnAnyUserInner(String packageName, boolean callingUserOnly) {
+ if (packageName == null) {
+ return false;
+ }
+ final ComponentName deviceOwner = getDeviceOwnerComponentInner(callingUserOnly);
+ if (deviceOwner == null) {
+ return false;
+ }
+ return packageName.equals(deviceOwner.getPackageName());
+ }
+
+ private ComponentName getDeviceOwnerComponentInner(boolean callingUserOnly) {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerComponent(callingUserOnly);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return ID of the user who runs device owner, or {@link UserHandle#USER_NULL} if there's
+ * no device owner.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public int getDeviceOwnerUserId() {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerUserId();
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+ }
+ }
+ return UserHandle.USER_NULL;
}
/**
@@ -2699,46 +2879,43 @@
}
/**
- * Returns the device owner package name. Note this method will still return the device owner
- * package name even if it's running on a different user.
+ * Returns the device owner package name, only if it's running on the calling user.
+ *
+ * <p>Bundled components should use {@code getDeviceOwnerComponentOnCallingUser()} for clarity.
*
* @hide
*/
@SystemApi
public String getDeviceOwner() {
- final ComponentName componentName = getDeviceOwnerComponent();
- return componentName == null ? null : componentName.getPackageName();
+ final ComponentName name = getDeviceOwnerComponentOnCallingUser();
+ return name != null ? name.getPackageName() : null;
}
/**
- * Returns the device owner name. Note this method will still return the device owner
- * name even if it's running on a different user.
+ * @return true if the device is managed by any device owner.
+ *
+ * <p>Requires the MANAGE_USERS permission.
*
* @hide
*/
- public String getDeviceOwnerName() {
+ public boolean isDeviceManaged() {
+ return getDeviceOwnerComponentOnAnyUser() != null;
+ }
+
+ /**
+ * Returns the device owner name. Note this method *will* return the device owner
+ * name when it's running on a different user.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public String getDeviceOwnerNameOnAnyUser() {
if (mService != null) {
try {
return mService.getDeviceOwnerName();
} catch (RemoteException re) {
- Log.w(TAG, "Failed to get device owner");
- }
- }
- return null;
- }
-
- /**
- * Returns the device owner component name. Note this method will still return the device owner
- * component name even if it's running on a different user.
- *
- * @hide
- */
- public ComponentName getDeviceOwnerComponent() {
- if (mService != null) {
- try {
- return mService.getDeviceOwner();
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get device owner");
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
}
}
return null;
@@ -2851,9 +3028,6 @@
*/
public boolean setProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName,
int userHandle) throws IllegalArgumentException {
- if (admin == null) {
- throw new NullPointerException("admin cannot be null");
- }
if (mService != null) {
try {
if (ownerName == null) {
@@ -2869,6 +3043,42 @@
}
/**
+ * Sets the device owner information to be shown on the lock screen.
+ *
+ * <p>If the device owner information is {@code null} or empty then the device owner info is
+ * cleared and the user owner info is shown on the lock screen if it is set.
+ *
+ * @param admin The name of the admin component to check.
+ * @param info Device owner information which will be displayed instead of the user
+ * owner info.
+ * @return Whether the device owner information has been set.
+ */
+ public boolean setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, String info) {
+ if (mService != null) {
+ try {
+ return mService.setDeviceOwnerLockScreenInfo(admin, info);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return The device owner information. If it is not set returns {@code null}.
+ */
+ public String getDeviceOwnerLockScreenInfo() {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerLockScreenInfo();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+ return null;
+ }
+
+ /**
* Sets the enabled state of the profile. A profile should be enabled only once it is ready to
* be used. Only the profile owner can call this.
*
@@ -3210,11 +3420,11 @@
* @hide
*/
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
- Intent originalIntent) {
+ long directoryId, Intent originalIntent) {
if (mService != null) {
try {
mService.startManagedQuickContact(
- actualLookupKey, actualContactId, originalIntent);
+ actualLookupKey, actualContactId, directoryId, originalIntent);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -3222,6 +3432,16 @@
}
/**
+ * Start Quick Contact on the managed profile for the current user, if the policy allows.
+ * @hide
+ */
+ public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+ Intent originalIntent) {
+ startManagedQuickContact(actualLookupKey, actualContactId, Directory.DEFAULT,
+ originalIntent);
+ }
+
+ /**
* Called by a profile owner of a managed profile to set whether bluetooth
* devices can access enterprise contacts.
* <p>
@@ -3481,6 +3701,48 @@
}
/**
+ * Called by a device owner to get the list of apps to keep around as APKs even if no user has
+ * currently installed it.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ *
+ * @return List of package names to keep cached.
+ * @hide
+ */
+ public List<String> getKeepUninstalledPackages(@NonNull ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getKeepUninstalledPackages(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called by a device owner to set a list of apps to keep around as APKs even if no user has
+ * currently installed it.
+ *
+ * <p>Please note that setting this policy does not imply that specified apps will be
+ * automatically pre-cached.</p>
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageNames List of package names to keep cached.
+ * @hide
+ */
+ public void setKeepUninstalledPackages(@NonNull ComponentName admin,
+ @NonNull List<String> packageNames) {
+ if (mService != null) {
+ try {
+ mService.setKeepUninstalledPackages(admin, packageNames);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Called by a device owner to create a user with the specified name. The UserHandle returned
* by this method should not be persisted as user handles are recycled as users are removed and
* created. If you need to persist an identifier for this user, use
@@ -3653,12 +3915,18 @@
* {@link UserManager#getUserRestrictions()}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if the {@code admin} is not an active admin.
*/
public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+ return getUserRestrictions(admin, myUserId());
+ }
+
+ /** @hide per-user version */
+ public Bundle getUserRestrictions(@NonNull ComponentName admin, int userHandle) {
Bundle ret = null;
if (mService != null) {
try {
- ret = mService.getUserRestrictions(admin);
+ ret = mService.getUserRestrictions(admin, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -4348,4 +4616,54 @@
return false;
}
}
+
+ /**
+ * @hide
+ * Return if this user is a managed profile of another user. An admin can become the profile
+ * owner of a managed profile with {@link #ACTION_PROVISION_MANAGED_PROFILE} and of a managed
+ * user with {@link #ACTION_PROVISION_MANAGED_USER}.
+ * @param admin Which profile owner this request is associated with.
+ * @return if this user is a managed profile of another user.
+ */
+ public boolean isManagedProfile(@NonNull ComponentName admin) {
+ try {
+ return mService.isManagedProfile(admin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ * Return if this user is a system-only user. An admin can manage a device from a system only
+ * user by calling {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE}.
+ * @param admin Which device owner this request is associated with.
+ * @return if this user is a system-only user.
+ */
+ public boolean isSystemOnlyUser(@NonNull ComponentName admin) {
+ try {
+ return mService.isSystemOnlyUser(admin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return false;
+ }
+ }
+
+ /**
+ * Called by device owner to get the MAC address of the Wi-Fi device.
+ *
+ * @return the MAC address of the Wi-Fi device, or null when the information is not
+ * available. (For example, Wi-Fi hasn't been enabled, or the device doesn't support Wi-Fi.)
+ *
+ * <p>The address will be in the {@code XX:XX:XX:XX:XX:XX} format.
+ */
+ public String getWifiMacAddress() {
+ try {
+ return mService.getWifiMacAddress();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 5a46cd5..0a0d77d 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,8 +16,6 @@
package android.app.admin;
-import android.os.Bundle;
-
import java.util.List;
/**
@@ -71,19 +69,4 @@
* @return true if the uid is an active admin with the given policy.
*/
public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
-
- /**
- * Takes a {@link Bundle} containing "base" user restrictions stored in
- * {@link com.android.server.pm.UserManagerService}, mixes restrictions set by the device owner
- * and the profile owner and returns the merged restrictions.
- *
- * This method always returns a new {@link Bundle}.
- */
- public abstract Bundle getComposedUserRestrictions(int userId, Bundle inBundle);
-
- /**
- * @return true if a package is a device admin (possibly DO or PO) running on
- * user {@code userId}.
- */
- public abstract boolean isDeviceAdminPackage(int userId, String packageName);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 95a22ef..6b4567c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -81,7 +81,7 @@
void lockNow();
- void wipeData(int flags, int userHandle);
+ void wipeData(int flags);
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
ComponentName getGlobalProxyAdmin(int userHandle);
@@ -114,10 +114,10 @@
void reportSuccessfulPasswordAttempt(int userHandle);
boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
- boolean isDeviceOwnerPackage(String packageName);
- ComponentName getDeviceOwner();
+ ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
+ int getDeviceOwnerUserId();
boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
ComponentName getProfileOwner(int userHandle);
@@ -127,6 +127,9 @@
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
+ boolean setDeviceOwnerLockScreenInfo(in ComponentName who, String deviceOwnerInfo);
+ String getDeviceOwnerLockScreenInfo();
+
boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, in String[] aliases);
void enforceCanManageCaCerts(in ComponentName admin);
@@ -147,7 +150,7 @@
ComponentName getRestrictionsProvider(int userHandle);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
- Bundle getUserRestrictions(in ComponentName who);
+ Bundle getUserRestrictions(in ComponentName who, int userId);
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
@@ -192,7 +195,7 @@
void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
boolean getCrossProfileCallerIdDisabled(in ComponentName who);
boolean getCrossProfileCallerIdDisabledForUser(int userId);
- void startManagedQuickContact(String lookupKey, long contactId, in Intent originalIntent);
+ void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
boolean getBluetoothContactSharingDisabled(in ComponentName who);
@@ -229,4 +232,9 @@
String permission, int grantState);
int getPermissionGrantState(in ComponentName admin, String packageName, String permission);
boolean isProvisioningAllowed(String action);
+ void setKeepUninstalledPackages(in ComponentName admin,in List<String> packageList);
+ List<String> getKeepUninstalledPackages(in ComponentName admin);
+ boolean isManagedProfile(in ComponentName admin);
+ boolean isSystemOnlyUser(in ComponentName admin);
+ String getWifiMacAddress();
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 25d9aa9..09a15de 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -685,6 +685,48 @@
}
/**
+ * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
+ * audio to the HF unless explicitly told to.
+ * This method should be used in cases where the SCO channel is shared between multiple profiles
+ * and must be delegated by a source knowledgeable
+ * Note: This is an internal function and shouldn't be exposed
+ *
+ * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public void setAudioRouteAllowed(boolean allowed) {
+ if (VDBG) log("setAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.setAudioRouteAllowed(allowed);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
+ * Note: This is an internal function and shouldn't be exposed
+ *
+ * @hide
+ */
+ public boolean getAudioRouteAllowed() {
+ if (VDBG) log("getAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getAudioRouteAllowed();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
* Check if Bluetooth SCO audio is connected.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 0e23fad..0bb4088 100755
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -50,6 +50,8 @@
boolean isAudioOn();
boolean connectAudio();
boolean disconnectAudio();
+ void setAudioRouteAllowed(boolean allowed);
+ boolean getAudioRouteAllowed();
boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device);
boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device);
void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 68b77fe..4a7cbc7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -18,6 +18,7 @@
import android.content.pm.ApplicationInfo;
import android.os.ResultReceiver;
+import android.os.ShellCommand;
import android.provider.MediaStore;
import android.util.ArraySet;
@@ -57,11 +58,13 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -1423,6 +1426,36 @@
public static final String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
/**
+ * Activity Action: Launch ephemeral installer.
+ * <p>
+ * Input: The data must be a http: URI that the ephemeral application is registered
+ * to handle.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE
+ = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
+
+ /**
+ * Service Action: Resolve ephemeral application.
+ * <p>
+ * The system will have a persistent connection to this service.
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE
+ = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
+
+ /**
* Used as a string extra field with {@link #ACTION_INSTALL_PACKAGE} to install a
* package. Specifies the installer package name; this package will receive the
* {@link #ACTION_APP_ERROR} intent.
@@ -2923,8 +2956,8 @@
* multiple selection), then you can specify {@link #EXTRA_ALLOW_MULTIPLE}
* to indicate this.
* <p>
- * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
- * returned URIs can be opened with
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent to obtain
+ * URIs that can be opened with
* {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was picked, returned in
@@ -2959,8 +2992,8 @@
* Callers can provide an initial display name through {@link #EXTRA_TITLE},
* but the user may change this value before creating the file.
* <p>
- * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
- * returned URIs can be opened with
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent to obtain
+ * URIs that can be opened with
* {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was created. This must be a
@@ -3054,21 +3087,21 @@
/**
* Thermal state when the device is normal. This state is sent in the
- * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
* {@hide}
*/
public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
/**
* Thermal state where the device is approaching its maximum threshold. This state is sent in
- * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * the {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
* {@hide}
*/
public static final int EXTRA_THERMAL_STATE_WARNING = 1;
/**
* Thermal state where the device has reached its maximum threshold. This state is sent in the
- * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}.
* {@hide}
*/
public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
@@ -5083,6 +5116,437 @@
return intent;
}
+ /** @hide */
+ public interface CommandOptionHandler {
+ boolean handleOption(String opt, ShellCommand cmd);
+ }
+
+ /** @hide */
+ public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
+ throws URISyntaxException {
+ Intent intent = new Intent();
+ Intent baseIntent = intent;
+ boolean hasIntentInfo = false;
+
+ Uri data = null;
+ String type = null;
+
+ String opt;
+ while ((opt=cmd.getNextOption()) != null) {
+ switch (opt) {
+ case "-a":
+ intent.setAction(cmd.getNextArgRequired());
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ break;
+ case "-d":
+ data = Uri.parse(cmd.getNextArgRequired());
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ break;
+ case "-t":
+ type = cmd.getNextArgRequired();
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ break;
+ case "-c":
+ intent.addCategory(cmd.getNextArgRequired());
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ break;
+ case "-e":
+ case "--es": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, value);
+ }
+ break;
+ case "--esn": {
+ String key = cmd.getNextArgRequired();
+ intent.putExtra(key, (String) null);
+ }
+ break;
+ case "--ei": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, Integer.decode(value));
+ }
+ break;
+ case "--eu": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, Uri.parse(value));
+ }
+ break;
+ case "--ecn": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ ComponentName cn = ComponentName.unflattenFromString(value);
+ if (cn == null)
+ throw new IllegalArgumentException("Bad component name: " + value);
+ intent.putExtra(key, cn);
+ }
+ break;
+ case "--eia": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ int[] list = new int[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ list[i] = Integer.decode(strings[i]);
+ }
+ intent.putExtra(key, list);
+ }
+ break;
+ case "--eial": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ ArrayList<Integer> list = new ArrayList<>(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ list.add(Integer.decode(strings[i]));
+ }
+ intent.putExtra(key, list);
+ }
+ break;
+ case "--el": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, Long.valueOf(value));
+ }
+ break;
+ case "--ela": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ long[] list = new long[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ list[i] = Long.valueOf(strings[i]);
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--elal": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ ArrayList<Long> list = new ArrayList<>(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ list.add(Long.valueOf(strings[i]));
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--ef": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, Float.valueOf(value));
+ hasIntentInfo = true;
+ }
+ break;
+ case "--efa": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ float[] list = new float[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ list[i] = Float.valueOf(strings[i]);
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--efal": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ ArrayList<Float> list = new ArrayList<>(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ list.add(Float.valueOf(strings[i]));
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--esa": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ // Split on commas unless they are preceeded by an escape.
+ // The escape character must be escaped for the string and
+ // again for the regex, thus four escape characters become one.
+ String[] strings = value.split("(?<!\\\\),");
+ intent.putExtra(key, strings);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--esal": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ // Split on commas unless they are preceeded by an escape.
+ // The escape character must be escaped for the string and
+ // again for the regex, thus four escape characters become one.
+ String[] strings = value.split("(?<!\\\\),");
+ ArrayList<String> list = new ArrayList<>(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ list.add(strings[i]);
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--ez": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired().toLowerCase();
+ // Boolean.valueOf() results in false for anything that is not "true", which is
+ // error-prone in shell commands
+ boolean arg;
+ if ("true".equals(value) || "t".equals(value)) {
+ arg = true;
+ } else if ("false".equals(value) || "f".equals(value)) {
+ arg = false;
+ } else {
+ try {
+ arg = Integer.decode(value) != 0;
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("Invalid boolean value: " + value);
+ }
+ }
+
+ intent.putExtra(key, arg);
+ }
+ break;
+ case "-n": {
+ String str = cmd.getNextArgRequired();
+ ComponentName cn = ComponentName.unflattenFromString(str);
+ if (cn == null)
+ throw new IllegalArgumentException("Bad component name: " + str);
+ intent.setComponent(cn);
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ }
+ break;
+ case "-p": {
+ String str = cmd.getNextArgRequired();
+ intent.setPackage(str);
+ if (intent == baseIntent) {
+ hasIntentInfo = true;
+ }
+ }
+ break;
+ case "-f":
+ String str = cmd.getNextArgRequired();
+ intent.setFlags(Integer.decode(str).intValue());
+ break;
+ case "--grant-read-uri-permission":
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ break;
+ case "--grant-write-uri-permission":
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ break;
+ case "--grant-persistable-uri-permission":
+ intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ break;
+ case "--grant-prefix-uri-permission":
+ intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ break;
+ case "--exclude-stopped-packages":
+ intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
+ break;
+ case "--include-stopped-packages":
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ break;
+ case "--debug-log-resolution":
+ intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+ break;
+ case "--activity-brought-to-front":
+ intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ break;
+ case "--activity-clear-top":
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ break;
+ case "--activity-clear-when-task-reset":
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ break;
+ case "--activity-exclude-from-recents":
+ intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ break;
+ case "--activity-launched-from-history":
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ break;
+ case "--activity-multiple-task":
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ break;
+ case "--activity-no-animation":
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ break;
+ case "--activity-no-history":
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+ break;
+ case "--activity-no-user-action":
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ break;
+ case "--activity-previous-is-top":
+ intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+ break;
+ case "--activity-reorder-to-front":
+ intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ break;
+ case "--activity-reset-task-if-needed":
+ intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ break;
+ case "--activity-single-top":
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ break;
+ case "--activity-clear-task":
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ break;
+ case "--activity-task-on-home":
+ intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ break;
+ case "--receiver-registered-only":
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ break;
+ case "--receiver-replace-pending":
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ break;
+ case "--selector":
+ intent.setDataAndType(data, type);
+ intent = new Intent();
+ break;
+ default:
+ if (optionHandler != null && optionHandler.handleOption(opt, cmd)) {
+ // Okay, caller handled this option.
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ break;
+ }
+ }
+ intent.setDataAndType(data, type);
+
+ final boolean hasSelector = intent != baseIntent;
+ if (hasSelector) {
+ // A selector was specified; fix up.
+ baseIntent.setSelector(intent);
+ intent = baseIntent;
+ }
+
+ String arg = cmd.getNextArg();
+ baseIntent = null;
+ if (arg == null) {
+ if (hasSelector) {
+ // If a selector has been specified, and no arguments
+ // have been supplied for the main Intent, then we can
+ // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't
+ // need to have a component name specified yet, the
+ // selector will take care of that.
+ baseIntent = new Intent(Intent.ACTION_MAIN);
+ baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ }
+ } else if (arg.indexOf(':') >= 0) {
+ // The argument is a URI. Fully parse it, and use that result
+ // to fill in any data not specified so far.
+ baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME
+ | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE);
+ } else if (arg.indexOf('/') >= 0) {
+ // The argument is a component name. Build an Intent to launch
+ // it.
+ baseIntent = new Intent(Intent.ACTION_MAIN);
+ baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ baseIntent.setComponent(ComponentName.unflattenFromString(arg));
+ } else {
+ // Assume the argument is a package name.
+ baseIntent = new Intent(Intent.ACTION_MAIN);
+ baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ baseIntent.setPackage(arg);
+ }
+ if (baseIntent != null) {
+ Bundle extras = intent.getExtras();
+ intent.replaceExtras((Bundle)null);
+ Bundle uriExtras = baseIntent.getExtras();
+ baseIntent.replaceExtras((Bundle)null);
+ if (intent.getAction() != null && baseIntent.getCategories() != null) {
+ HashSet<String> cats = new HashSet<String>(baseIntent.getCategories());
+ for (String c : cats) {
+ baseIntent.removeCategory(c);
+ }
+ }
+ intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
+ if (extras == null) {
+ extras = uriExtras;
+ } else if (uriExtras != null) {
+ uriExtras.putAll(extras);
+ extras = uriExtras;
+ }
+ intent.replaceExtras(extras);
+ hasIntentInfo = true;
+ }
+
+ if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
+ return intent;
+ }
+
+ /** @hide */
+ public static void printIntentArgsHelp(PrintWriter pw, String prefix) {
+ final String[] lines = new String[] {
+ "<INTENT> specifications include these flags and arguments:",
+ " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]",
+ " [-c <CATEGORY> [-c <CATEGORY>] ...]",
+ " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]",
+ " [--esn <EXTRA_KEY> ...]",
+ " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]",
+ " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]",
+ " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]",
+ " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]",
+ " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]",
+ " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]",
+ " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
+ " (mutiple extras passed as Integer[])",
+ " [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
+ " (mutiple extras passed as List<Integer>)",
+ " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
+ " (mutiple extras passed as Long[])",
+ " [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
+ " (mutiple extras passed as List<Long>)",
+ " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
+ " (mutiple extras passed as Float[])",
+ " [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
+ " (mutiple extras passed as List<Float>)",
+ " [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
+ " (mutiple extras passed as String[]; to embed a comma into a string,",
+ " escape it using \"\\,\")",
+ " [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
+ " (mutiple extras passed as List<String>; to embed a comma into a string,",
+ " escape it using \"\\,\")",
+ " [--grant-read-uri-permission] [--grant-write-uri-permission]",
+ " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]",
+ " [--debug-log-resolution] [--exclude-stopped-packages]",
+ " [--include-stopped-packages]",
+ " [--activity-brought-to-front] [--activity-clear-top]",
+ " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]",
+ " [--activity-launched-from-history] [--activity-multiple-task]",
+ " [--activity-no-animation] [--activity-no-history]",
+ " [--activity-no-user-action] [--activity-previous-is-top]",
+ " [--activity-reorder-to-front] [--activity-reset-task-if-needed]",
+ " [--activity-single-top] [--activity-clear-task]",
+ " [--activity-task-on-home]",
+ " [--receiver-registered-only] [--receiver-replace-pending]",
+ " [--selector]",
+ " [<URI> | <PACKAGE> | <COMPONENT>]"
+ };
+ for (String line : lines) {
+ pw.print(prefix);
+ pw.println(line);
+ }
+ }
+
/**
* Retrieve the general action to be performed, such as
* {@link #ACTION_VIEW}. The action describes the general way the rest of
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 885255f..4a3c59b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -627,9 +627,9 @@
* {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
* {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
* {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
- * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT} and
- * {@link #CONFIG_LAYOUT_DIRECTION}. Set from the {@link android.R.attr#configChanges}
- * attribute.
+ * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT},
+ * {@link #CONFIG_DENSITY}, and {@link #CONFIG_LAYOUT_DIRECTION}.
+ * Set from the {@link android.R.attr#configChanges} attribute.
*/
public int configChanges;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 52c2f9b..1996e0f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -22,7 +22,9 @@
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Printer;
@@ -469,6 +471,22 @@
public static final int PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED = 1 << 5;
/**
+ * When set, assume that all components under the given app are encryption
+ * aware, unless otherwise specified.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ENCRYPTION_AWARE = 1 << 6;
+
+ /**
+ * Value for {@link #privateFlags}: set to {@code true} if the application
+ * is AutoPlay.
+ *
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
+
+ /**
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
* {@hide}
*/
@@ -963,7 +981,8 @@
.getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
.getAbsolutePath();
- if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0) {
+ if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
+ && StorageManager.isFileBasedEncryptionEnabled()) {
dataDir = deviceEncryptedDataDir;
} else {
dataDir = credentialEncryptedDataDir;
@@ -1030,6 +1049,18 @@
&& (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
+ /** @hide */
+ public boolean isEncryptionAware() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE) != 0;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isAutoPlayApp() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_AUTOPLAY) != 0;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
index a5a8e3f..084bc18 100644
--- a/core/java/android/content/pm/AppsQueryHelper.java
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -18,13 +18,11 @@
import android.Manifest;
import android.app.AppGlobals;
-import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethod;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,36 +42,37 @@
public static int GET_NON_LAUNCHABLE_APPS = 1;
/**
- * Return apps with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission
+ * Return apps with {@link Manifest.permission#INTERACT_ACROSS_USERS} permission
*/
public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
/**
- * Return all input methods that are marked as default.
- * <p>When this flag is set, {@code user} specified in
- * {@link #queryApps(int, boolean, UserHandle)} must be
- * {@link UserHandle#myUserId user of the current process}.
+ * Return all input methods available for the current user.
*/
- public static int GET_DEFAULT_IMES = 1 << 2;
+ public static int GET_IMES = 1 << 2;
- private final Context mContext;
+ private final IPackageManager mPackageManager;
private List<ApplicationInfo> mAllApps;
- public AppsQueryHelper(Context context) {
- mContext = context;
+ public AppsQueryHelper(IPackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ public AppsQueryHelper() {
+ this(AppGlobals.getPackageManager());
}
/**
* Return a List of all packages that satisfy a specified criteria.
* @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
- * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_DEFAULT_IMES}.
+ * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_IMES}.
* @param systemAppsOnly if true, only system apps will be returned
* @param user user, whose apps are queried
*/
public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
- boolean defaultImes = (flags & GET_DEFAULT_IMES) > 0;
+ boolean imes = (flags & GET_IMES) > 0;
if (mAllApps == null) {
mAllApps = getAllApps(user.getIdentifier());
}
@@ -113,7 +112,6 @@
}
}
}
-
if (interactAcrossUsers) {
final List<PackageInfo> packagesHoldingPermissions = getPackagesHoldingPermission(
Manifest.permission.INTERACT_ACROSS_USERS, user.getIdentifier());
@@ -129,29 +127,18 @@
}
}
- if (defaultImes) {
- if (UserHandle.myUserId() != user.getIdentifier()) {
- throw new IllegalArgumentException("Specified user handle " + user
- + " is not a user of the current process.");
- }
- List<InputMethodInfo> imis = getInputMethodList();
- int imisSize = imis.size();
- ArraySet<String> defaultImePackages = new ArraySet<>();
- for (int i = 0; i < imisSize; i++) {
- InputMethodInfo imi = imis.get(i);
- if (imi.isDefault(mContext)) {
- defaultImePackages.add(imi.getPackageName());
- }
- }
- final int allAppsSize = mAllApps.size();
- for (int i = 0; i < allAppsSize; i++) {
- final ApplicationInfo appInfo = mAllApps.get(i);
- if (systemAppsOnly && !appInfo.isSystemApp()) {
+ if (imes) {
+ final List<ResolveInfo> resolveInfos = queryIntentServicesAsUser(
+ new Intent(InputMethod.SERVICE_INTERFACE), user.getIdentifier());
+ final int resolveInfosSize = resolveInfos.size();
+
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
+ if (systemAppsOnly && !serviceInfo.applicationInfo.isSystemApp()) {
continue;
}
- final String packageName = appInfo.packageName;
- if (defaultImePackages.contains(packageName)) {
- result.add(packageName);
+ if (!result.contains(serviceInfo.packageName)) {
+ result.add(serviceInfo.packageName);
}
}
}
@@ -163,9 +150,8 @@
@SuppressWarnings("unchecked")
protected List<ApplicationInfo> getAllApps(int userId) {
try {
- return AppGlobals.getPackageManager().getInstalledApplications(
- PackageManager.GET_UNINSTALLED_PACKAGES
- | PackageManager.GET_DISABLED_COMPONENTS, userId).getList();
+ return mPackageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS, userId).getList();
} catch (RemoteException e) {
throw new IllegalStateException("Package manager has died", e);
}
@@ -173,17 +159,22 @@
@VisibleForTesting
protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
- return mContext.getPackageManager()
- .queryIntentActivitiesAsUser(intent, PackageManager.GET_DISABLED_COMPONENTS
- | PackageManager.GET_UNINSTALLED_PACKAGES, userId);
+ try {
+ return mPackageManager.queryIntentActivities(intent, null,
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES,
+ userId);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager has died", e);
+ }
}
@VisibleForTesting
- @SuppressWarnings("unchecked")
- protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
+ protected List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int userId) {
try {
- return AppGlobals.getPackageManager().getPackagesHoldingPermissions(new String[]{perm},
- 0, userId).getList();
+ return mPackageManager.queryIntentServices(intent, null,
+ PackageManager.GET_META_DATA
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
} catch (RemoteException e) {
throw new IllegalStateException("Package manager has died", e);
}
@@ -191,9 +182,13 @@
@VisibleForTesting
@SuppressWarnings("unchecked")
- protected List<InputMethodInfo> getInputMethodList() {
- InputMethodManager imm = (InputMethodManager)
- mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
- return imm.getInputMethodList();
+ protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
+ try {
+ return mPackageManager.getPackagesHoldingPermissions(new String[]{perm}, 0,
+ userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager has died", e);
+ }
}
+
}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index d9005b2..6586426 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Bundle;
@@ -31,7 +32,7 @@
interface ILauncherApps {
void addOnAppsChangedListener(in IOnAppsChangedListener listener);
void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
- List<ResolveInfo> getLauncherActivities(String packageName, in UserHandle user);
+ ParceledListSlice getLauncherActivities(String packageName, in UserHandle user);
ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fec2c44..6fe1efd 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -420,10 +420,14 @@
boolean hasSystemUidErrors();
/**
- * Ask the package manager to perform boot-time dex-opt of all
- * existing packages.
+ * Ask the package manager to fstrim the disk if needed.
*/
- void performBootDexOpt();
+ void performFstrimIfNeeded();
+
+ /**
+ * Notify the package manager that a package is going to be used.
+ */
+ void notifyPackageUse(String packageName);
/**
* Ask the package manager to perform dex-opt (if needed) on the given
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 6827d7a..a5617b4 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -41,7 +41,6 @@
private ComponentName mComponentName;
private ResolveInfo mResolveInfo;
private UserHandle mUser;
- private long mFirstInstallTime;
/**
* Create a launchable activity object for a given ResolveInfo and user.
@@ -50,14 +49,12 @@
* @param info ResolveInfo from which to create the LauncherActivityInfo.
* @param user The UserHandle of the profile to which this activity belongs.
*/
- LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user,
- long firstInstallTime) {
+ LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
this(context);
mResolveInfo = info;
mActivityInfo = info.activityInfo;
mComponentName = LauncherApps.getComponentName(info);
mUser = user;
- mFirstInstallTime = firstInstallTime;
}
LauncherActivityInfo(Context context) {
@@ -136,7 +133,7 @@
/**
* Returns the drawable for this activity, without any badging for the profile.
- * @param resource id of the drawable.
+ * @param iconRes id of the drawable.
* @param density The preferred density of the icon, zero for default density. Use
* density DPI values from {@link DisplayMetrics}.
* @see DisplayMetrics
@@ -179,7 +176,13 @@
* @return The time of installation of the package, in milliseconds.
*/
public long getFirstInstallTime() {
- return mFirstInstallTime;
+ try {
+ return mPm.getPackageInfo(mActivityInfo.packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
+ } catch (NameNotFoundException nnfe) {
+ // Sorry, can't find package
+ return 0;
+ }
}
/**
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index e9ec771..6e67af4 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -142,28 +142,18 @@
* @return List of launchable activities. Can be an empty list but will not be null.
*/
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
- List<ResolveInfo> activities = null;
+ ParceledListSlice<ResolveInfo> activities = null;
try {
activities = mService.getLauncherActivities(packageName, user);
} catch (RemoteException re) {
- throw new RuntimeException("Failed to call LauncherAppsService");
+ throw new RuntimeException("Failed to call LauncherAppsService", re);
}
if (activities == null) {
return Collections.EMPTY_LIST;
}
ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
- final int count = activities.size();
- for (int i = 0; i < count; i++) {
- ResolveInfo ri = activities.get(i);
- long firstInstallTime = 0;
- try {
- firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
- } catch (NameNotFoundException nnfe) {
- // Sorry, can't find package
- }
- LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
- firstInstallTime);
+ for (ResolveInfo ri : activities.getList()) {
+ LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
if (DEBUG) {
Log.v(TAG, "Returning activity for profile " + user + " : "
+ lai.getComponentName());
@@ -189,19 +179,11 @@
try {
ResolveInfo ri = mService.resolveActivity(intent, user);
if (ri != null) {
- long firstInstallTime = 0;
- try {
- firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;
- } catch (NameNotFoundException nnfe) {
- // Sorry, can't find package
- }
- LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user,
- firstInstallTime);
+ LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
return info;
}
} catch (RemoteException re) {
- throw new RuntimeException("Failed to call LauncherAppsService");
+ throw new RuntimeException("Failed to call LauncherAppsService", re);
}
return null;
}
@@ -256,7 +238,7 @@
try {
return mService.isPackageEnabled(packageName, user);
} catch (RemoteException re) {
- throw new RuntimeException("Failed to call LauncherAppsService");
+ throw new RuntimeException("Failed to call LauncherAppsService", re);
}
}
@@ -272,7 +254,7 @@
try {
return mService.isActivityEnabled(component, user);
} catch (RemoteException re) {
- throw new RuntimeException("Failed to call LauncherAppsService");
+ throw new RuntimeException("Failed to call LauncherAppsService", re);
}
}
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
index 1fbef7a..e7dc764 100644
--- a/core/java/android/content/pm/ManifestDigest.java
+++ b/core/java/android/content/pm/ManifestDigest.java
@@ -16,6 +16,8 @@
package android.content.pm;
+import com.android.internal.util.HexDump;
+
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -118,7 +120,7 @@
final int N = mDigest.length;
for (int i = 0; i < N; i++) {
final byte b = mDigest[i];
- IntegralToString.appendByteAsHex(sb, b, false);
+ HexDump.appendByteAsHex(sb, b, false);
sb.append(',');
}
sb.append('}');
@@ -142,4 +144,4 @@
}
};
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 566de4e..0c28008 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -240,16 +240,15 @@
public static final int GET_ENCRYPTION_UNAWARE_COMPONENTS = 0x00040000;
/**
- * {@link PackageInfo} flag: return components as if the given user is
- * running with amnesia. This typically limits the component to only those
- * marked as {@link ComponentInfo#encryptionAware}, unless
+ * {@link PackageInfo} flag: return components that are marked as
+ * {@link ComponentInfo#encryptionAware}, unless
* {@link #GET_ENCRYPTION_UNAWARE_COMPONENTS} is also specified.
* <p>
* This flag is for internal use only.
*
* @hide
*/
- public static final int FLAG_USER_RUNNING_WITH_AMNESIA = 0x00080000;
+ public static final int MATCH_ENCRYPTION_AWARE_ONLY = 0x00080000;
/**
* Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
@@ -1730,6 +1729,8 @@
* {@link #hasSystemFeature}: The device supports freeform window management.
* Windows have title bars and can be moved and resized.
*/
+ // If this feature is present, you also need to set
+ // com.android.internal.R.config_freeformWindowManagement to true in your configuration overlay.
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT
= "android.software.freeform_window_management";
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index bf70d6c..905ac5e 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import android.annotation.NonNull;
+import java.util.List;
/**
* Package manager local system service interface.
@@ -115,4 +115,11 @@
*/
public abstract void grantDefaultPermissionsToDefaultSimCallManager(String packageName,
int userId);
+
+ /**
+ * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
+ * currently installed it. The apps are not preloaded.
+ * @param packageList List of package names to keep cached.
+ */
+ public abstract void setKeepUninstalledPackages(List<String> packageList);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f176d89..838da37 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2636,6 +2636,10 @@
&& (flags & PARSE_IS_SYSTEM) != 0) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED;
}
+ if (sa.getBoolean(R.styleable.AndroidManifestApplication_encryptionAware, false)
+ && (flags & PARSE_IS_SYSTEM) != 0) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE;
+ }
String str;
str = sa.getNonConfigurationString(
@@ -3236,7 +3240,8 @@
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
a.info.encryptionAware = sa.getBoolean(
- R.styleable.AndroidManifestActivity_encryptionAware, false);
+ R.styleable.AndroidManifestActivity_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
@@ -3253,7 +3258,8 @@
}
a.info.encryptionAware = sa.getBoolean(
- R.styleable.AndroidManifestActivity_encryptionAware, false);
+ R.styleable.AndroidManifestActivity_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
}
sa.recycle();
@@ -3655,7 +3661,8 @@
}
p.info.encryptionAware = sa.getBoolean(
- R.styleable.AndroidManifestProvider_encryptionAware, false);
+ R.styleable.AndroidManifestProvider_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
sa.recycle();
@@ -3938,7 +3945,8 @@
}
s.info.encryptionAware = sa.getBoolean(
- R.styleable.AndroidManifestService_encryptionAware, false);
+ R.styleable.AndroidManifestService_encryptionAware,
+ owner.applicationInfo.isEncryptionAware());
sa.recycle();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0606e35..7b3dde4 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -48,7 +48,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Trace;
-import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LocaleList;
@@ -68,7 +67,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.lang.ref.WeakReference;
import java.util.Locale;
/**
@@ -120,9 +118,6 @@
private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
sPreloadedColorStateLists = new LongSparseArray<>();
- private static final String CACHE_NOT_THEMED = "";
- private static final String CACHE_NULL_THEME = "null_theme";
-
// Pool of TypedArrays targeted to this Resources object.
final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
@@ -130,10 +125,11 @@
static Resources mSystem = null;
private static boolean sPreloaded;
- private static int sPreloadedDensity;
+
+ /** Lock object used to protect access to caches and configuration. */
+ private final Object mAccessLock = new Object();
// These are protected by mAccessLock.
- private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
private final DrawableCache mDrawableCache = new DrawableCache(this);
private final DrawableCache mColorDrawableCache = new DrawableCache(this);
@@ -147,7 +143,12 @@
/** Used to inflate drawable objects from XML. */
private DrawableInflater mDrawableInflater;
+ /** Lock object used to protect access to {@link #mTmpValue}. */
+ private final Object mTmpValueLock = new Object();
+
+ /** Single-item pool used to minimize TypedValue allocations. */
private TypedValue mTmpValue = new TypedValue();
+
private boolean mPreloading;
private int mLastCachedXmlBlockIndex = -1;
@@ -249,6 +250,10 @@
public NotFoundException(String name) {
super(name);
}
+
+ public NotFoundException(String name, Exception cause) {
+ super(name, cause);
+ }
}
/**
@@ -621,18 +626,15 @@
* @see #getDimensionPixelSize
*/
public float getDimension(@DimenRes int id) throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimension(value.data, mMetrics);
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -656,19 +658,15 @@
* @see #getDimensionPixelSize
*/
public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(
- value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(value.data, mMetrics);
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -693,19 +691,15 @@
* @see #getDimensionPixelOffset
*/
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(
- value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelSize(value.data, mMetrics);
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -727,18 +721,15 @@
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public float getFraction(@FractionRes int id, int base, int pbase) {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_FRACTION) {
return TypedValue.complexToFraction(value.data, base, pbase);
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -801,24 +792,14 @@
* not exist.
*/
@Nullable
- public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- } else {
- mTmpValue = null;
- }
- getValue(id, value, true);
+ public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
+ throws NotFoundException {
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
+ return loadDrawable(value, id, theme);
+ } finally {
+ releaseTempTypedValue(value);
}
- final Drawable res = loadDrawable(value, id, theme);
- synchronized (mAccessLock) {
- if (mTmpValue == null) {
- mTmpValue = value;
- }
- }
- return res;
}
/**
@@ -849,7 +830,8 @@
*/
@Deprecated
@Nullable
- public Drawable getDrawableForDensity(@DrawableRes int id, int density) throws NotFoundException {
+ public Drawable getDrawableForDensity(@DrawableRes int id, int density)
+ throws NotFoundException {
return getDrawableForDensity(id, density, null);
}
@@ -869,14 +851,8 @@
*/
@Nullable
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- } else {
- mTmpValue = null;
- }
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
getValueForDensity(id, density, value, true);
/*
@@ -893,15 +869,11 @@
value.density = (value.density * mMetrics.densityDpi) / density;
}
}
- }
- final Drawable res = loadDrawable(value, id, theme);
- synchronized (mAccessLock) {
- if (mTmpValue == null) {
- mTmpValue = value;
- }
+ return loadDrawable(value, id, theme);
+ } finally {
+ releaseTempTypedValue(value);
}
- return res;
}
/**
@@ -963,33 +935,21 @@
*/
@ColorInt
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
- mTmpValue = value;
return value.data;
} else if (value.type != TypedValue.TYPE_STRING) {
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
}
- mTmpValue = null;
- }
- final ColorStateList csl = loadColorStateList(value, id, theme);
- synchronized (mAccessLock) {
- if (mTmpValue == null) {
- mTmpValue = value;
- }
+ final ColorStateList csl = loadColorStateList(value, id, theme);
+ return csl.getDefaultColor();
+ } finally {
+ releaseTempTypedValue(value);
}
-
- return csl.getDefaultColor();
}
/**
@@ -1043,25 +1003,12 @@
@Nullable
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- } else {
- mTmpValue = null;
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
+ return loadColorStateList(value, id, theme);
+ } finally {
+ releaseTempTypedValue(value);
}
-
- final ColorStateList res = loadColorStateList(value, id, theme);
- synchronized (mAccessLock) {
- if (mTmpValue == null) {
- mTmpValue = value;
- }
- }
-
- return res;
}
/**
@@ -1078,19 +1025,16 @@
* @return Returns the boolean value contained in the resource.
*/
public boolean getBoolean(@BoolRes int id) throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type >= TypedValue.TYPE_FIRST_INT
- && value.type <= TypedValue.TYPE_LAST_INT) {
+ && value.type <= TypedValue.TYPE_LAST_INT) {
return value.data != 0;
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -1106,19 +1050,16 @@
* @return Returns the integer value contained in the resource.
*/
public int getInteger(@IntegerRes int id) throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type >= TypedValue.TYPE_FIRST_INT
- && value.type <= TypedValue.TYPE_LAST_INT) {
+ && value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -1136,17 +1077,15 @@
* @hide Pending API council approval.
*/
public float getFloat(int id) {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_FLOAT) {
return value.getFloat();
}
- throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
@@ -1238,22 +1177,60 @@
*
*/
public InputStream openRawResource(@RawRes int id) throws NotFoundException {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- } else {
+ final TypedValue value = obtainTempTypedValue();
+ try {
+ return openRawResource(id, value);
+ } finally {
+ releaseTempTypedValue(value);
+ }
+ }
+
+ /**
+ * Returns a TypedValue populated with data for the specified resource ID
+ * that's suitable for temporary use. The obtained TypedValue should be
+ * released using {@link #releaseTempTypedValue(TypedValue)}.
+ *
+ * @param id the resource ID for which data should be obtained
+ * @return a populated typed value suitable for temporary use
+ */
+ private TypedValue obtainTempTypedValue(@AnyRes int id) {
+ final TypedValue value = obtainTempTypedValue();
+ getValue(id, value, true);
+ return value;
+ }
+
+ /**
+ * Returns a TypedValue suitable for temporary use. The obtained TypedValue
+ * should be released using {@link #releaseTempTypedValue(TypedValue)}.
+ *
+ * @return a typed value suitable for temporary use
+ */
+ private TypedValue obtainTempTypedValue() {
+ TypedValue tmpValue = null;
+ synchronized (mTmpValueLock) {
+ if (mTmpValue != null) {
+ tmpValue = mTmpValue;
mTmpValue = null;
}
}
- InputStream res = openRawResource(id, value);
- synchronized (mAccessLock) {
+ if (tmpValue == null) {
+ return new TypedValue();
+ }
+ return tmpValue;
+ }
+
+ /**
+ * Returns a TypedValue to the pool. After calling this method, the
+ * specified TypedValue should no longer be accessed.
+ *
+ * @param value the typed value to return to the pool
+ */
+ private void releaseTempTypedValue(TypedValue value) {
+ synchronized (mTmpValueLock) {
if (mTmpValue == null) {
mTmpValue = value;
}
}
- return res;
}
/**
@@ -1307,32 +1284,14 @@
*/
public AssetFileDescriptor openRawResourceFd(@RawRes int id)
throws NotFoundException {
- TypedValue value;
- synchronized (mAccessLock) {
- value = mTmpValue;
- if (value == null) {
- value = new TypedValue();
- } else {
- mTmpValue = null;
- }
- getValue(id, value, true);
- }
+ final TypedValue value = obtainTempTypedValue(id);
try {
- return mAssets.openNonAssetFd(
- value.assetCookie, value.string.toString());
+ return mAssets.openNonAssetFd(value.assetCookie, value.string.toString());
} catch (Exception e) {
- NotFoundException rnf = new NotFoundException(
- "File " + value.string.toString()
- + " from drawable resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
+ throw new NotFoundException("File " + value.string.toString() + " from drawable "
+ + "resource ID #0x" + Integer.toHexString(id), e);
} finally {
- synchronized (mAccessLock) {
- if (mTmpValue == null) {
- mTmpValue = value;
- }
- }
+ releaseTempTypedValue(value);
}
}
@@ -2015,8 +1974,8 @@
Build.VERSION.RESOURCES_SDK_INT);
if (DEBUG_CONFIG) {
- Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
- + " final compat is " + mCompatibilityInfo);
+ Slog.i(TAG, "**** Updating config of " + this + ": final config is "
+ + mConfiguration + " final compat is " + mCompatibilityInfo);
}
mDrawableCache.onConfigurationChange(configChanges);
@@ -2402,8 +2361,7 @@
}
sPreloaded = true;
mPreloading = true;
- sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
- mConfiguration.densityDpi = sPreloadedDensity;
+ mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
updateConfiguration(null, null);
}
}
@@ -2740,19 +2698,16 @@
/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
throws NotFoundException {
- synchronized (mAccessLock) {
- TypedValue value = mTmpValue;
- if (value == null) {
- mTmpValue = value = new TypedValue();
- }
- getValue(id, value, true);
+ final TypedValue value = obtainTempTypedValue(id);
+ try {
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
- throw new NotFoundException(
- "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
- + Integer.toHexString(value.type) + " is not valid");
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ + " type #0x" + Integer.toHexString(value.type) + " is not valid");
+ } finally {
+ releaseTempTypedValue(value);
}
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 1fc69c0..73bb426 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3182,8 +3182,8 @@
}
/**
- * Sets GPS processing method. It will store up to 32 characters
- * in JPEG EXIF header.
+ * Sets GPS processing method. The method will be stored in a UTF-8 string up to 31 bytes
+ * long, in the JPEG EXIF header.
*
* @param processing_method The processing method to get this location.
*/
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a2ef078..6fc998f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2178,11 +2178,18 @@
* (8-14 bits is expected), or by the point where the sensor response
* becomes too non-linear to be useful. The default value for this is
* maximum representable value for a 16-bit raw sample (2^16 - 1).</p>
+ * <p>The white level values of captured images may vary for different
+ * capture settings (e.g., {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}). This key
+ * represents a coarse approximation for such case. It is recommended
+ * to use {@link CaptureResult#SENSOR_DYNAMIC_WHITE_LEVEL android.sensor.dynamicWhiteLevel} for captures when supported
+ * by the camera device, which provides more accurate white level values.</p>
* <p><b>Range of valid values:</b><br>
* > 255 (8-bit output)</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+ * @see CaptureResult#SENSOR_DYNAMIC_WHITE_LEVEL
+ * @see CaptureRequest#SENSOR_SENSITIVITY
*/
@PublicKey
public static final Key<Integer> SENSOR_INFO_WHITE_LEVEL =
@@ -2520,12 +2527,24 @@
* layout key (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}), i.e. the
* nth value given corresponds to the black level offset for the nth
* color channel listed in the CFA.</p>
+ * <p>The black level values of captured images may vary for different
+ * capture settings (e.g., {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}). This key
+ * represents a coarse approximation for such case. It is recommended to
+ * use {@link CaptureResult#SENSOR_DYNAMIC_BLACK_LEVEL android.sensor.dynamicBlackLevel} or use pixels from
+ * {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} directly for captures when
+ * supported by the camera device, which provides more accurate black
+ * level values. For raw capture in particular, it is recommended to use
+ * pixels from {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} to calculate black
+ * level values for each frame.</p>
* <p><b>Range of valid values:</b><br>
* >= 0 for each.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CaptureResult#SENSOR_DYNAMIC_BLACK_LEVEL
* @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
* @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+ * @see CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS
+ * @see CaptureRequest#SENSOR_SENSITIVITY
*/
@PublicKey
public static final Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN =
@@ -2580,6 +2599,32 @@
new Key<int[]>("android.sensor.availableTestPatternModes", int[].class);
/**
+ * <p>List of disjoint rectangles indicating the sensor
+ * optically shielded black pixel regions.</p>
+ * <p>In most camera sensors, the active array is surrounded by some
+ * optically shielded pixel areas. By blocking light, these pixels
+ * provides a reliable black reference for black level compensation
+ * in active array region.</p>
+ * <p>This key provides a list of disjoint rectangles specifying the
+ * regions of optically shielded (with metal shield) black pixel
+ * regions if the camera device is capable of reading out these black
+ * pixels in the output raw images. In comparison to the fixed black
+ * level values reported by {@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}, this key
+ * may provide a more accurate way for the application to calculate
+ * black level of each captured raw images.</p>
+ * <p>When this key is reported, the {@link CaptureResult#SENSOR_DYNAMIC_BLACK_LEVEL android.sensor.dynamicBlackLevel} and
+ * {@link CaptureResult#SENSOR_DYNAMIC_WHITE_LEVEL android.sensor.dynamicWhiteLevel} will also be reported.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+ * @see CaptureResult#SENSOR_DYNAMIC_BLACK_LEVEL
+ * @see CaptureResult#SENSOR_DYNAMIC_WHITE_LEVEL
+ */
+ @PublicKey
+ public static final Key<android.graphics.Rect[]> SENSOR_OPTICAL_BLACK_REGIONS =
+ new Key<android.graphics.Rect[]>("android.sensor.opticalBlackRegions", android.graphics.Rect[].class);
+
+ /**
* <p>List of lens shading modes for {@link CaptureRequest#SHADING_MODE android.shading.mode} that are supported by this camera device.</p>
* <p>This list contains lens shading modes that can be set for the camera device.
* Camera devices that support the MANUAL_POST_PROCESSING capability will always
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 35a1d96..f61892e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -472,13 +472,13 @@
* <li>The maximum available resolution for RAW_SENSOR streams
* will match either the value in
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li>
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</li>
* <li>All DNG-related optional metadata entries are provided
* by the camera device.</li>
* </ul>
*
- * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3f566eb..67835a0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -556,6 +556,10 @@
* Set a capture request field to a value. The field definitions can be
* found in {@link CaptureRequest}.
*
+ * <p>Setting a field to {@code null} will remove that field from the capture request.
+ * Unless the field is optional, removing it will likely produce an error from the camera
+ * device when the request is submitted.</p>
+ *
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index b3acf2b..5f27bca 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3292,6 +3292,69 @@
new Key<Long>("android.sensor.rollingShutterSkew", long.class);
/**
+ * <p>A per-frame dynamic black level offset for each of the color filter
+ * arrangement (CFA) mosaic channels.</p>
+ * <p>Camera sensor black levels may vary dramatically for different
+ * capture settings (e.g. {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}). The fixed black
+ * level reported by {@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern} may be too
+ * inaccurate to represent the actual value on a per-frame basis. The
+ * camera device internal pipeline relies on reliable black level values
+ * to process the raw images appropriately. To get the best image
+ * quality, the camera device may choose to estimate the per frame black
+ * level values either based on optically shielded black regions
+ * ({@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions}) or its internal model.</p>
+ * <p>This key reports the camera device estimated per-frame zero light
+ * value for each of the CFA mosaic channels in the camera sensor. The
+ * {@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern} may only represent a coarse
+ * approximation of the actual black level values. This value is the
+ * black level used in camera device internal image processing pipeline
+ * and generally more accurate than the fixed black level values.
+ * However, since they are estimated values by the camera device, they
+ * may not be as accurate as the black level values calculated from the
+ * optical black pixels reported by {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions}.</p>
+ * <p>The values are given in the same order as channels listed for the CFA
+ * layout key (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}), i.e. the
+ * nth value given corresponds to the black level offset for the nth
+ * color channel listed in the CFA.</p>
+ * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is
+ * available or the camera device advertises this key via
+ * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureRequestKeys }.</p>
+ * <p><b>Range of valid values:</b><br>
+ * >= 0 for each.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ * @see CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ */
+ @PublicKey
+ public static final Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_DYNAMIC_BLACK_LEVEL =
+ new Key<android.hardware.camera2.params.BlackLevelPattern>("android.sensor.dynamicBlackLevel", android.hardware.camera2.params.BlackLevelPattern.class);
+
+ /**
+ * <p>Maximum raw value output by sensor for this frame.</p>
+ * <p>Since the android.sensor.blackLevel may change for different
+ * capture settings (e.g., {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}), the white
+ * level will change accordingly. This key is similar to
+ * {@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}, but specifies the camera device
+ * estimated white level for each frame.</p>
+ * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is
+ * available or the camera device advertises this key via
+ * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureRequestKeys }.</p>
+ * <p><b>Range of valid values:</b><br>
+ * >= 0</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+ * @see CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ */
+ @PublicKey
+ public static final Key<Integer> SENSOR_DYNAMIC_WHITE_LEVEL =
+ new Key<Integer>("android.sensor.dynamicWhiteLevel", int.class);
+
+ /**
* <p>Quality of lens shading correction applied
* to the image data.</p>
* <p>When set to OFF mode, no lens shading correction will be applied by the
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ad9058f..a5f712e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2484,6 +2484,22 @@
}
/**
+ * Helper function to requests a network with a particular legacy type.
+ *
+ * This is temporarily public @hide so it can be called by system code that uses the
+ * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
+ * instead network notifications.
+ *
+ * TODO: update said system code to rely on NetworkCallbacks and make this method private.
+ *
+ * @hide
+ */
+ public void requestNetwork(NetworkCapabilities nc, NetworkCallback networkCallback,
+ int timeoutMs, int legacyType) {
+ sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType);
+ }
+
+ /**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
@@ -2513,8 +2529,8 @@
* {@code NetworkCapabilities}.
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
- REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ requestNetwork(request.networkCapabilities, networkCallback, 0,
+ inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
@@ -2537,12 +2553,15 @@
* this request.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#unavailable} is called.
+ *
+ * TODO: Make timeouts work and then unhide this method.
+ *
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs,
- REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ requestNetwork(request.networkCapabilities, networkCallback, timeoutMs,
+ inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 5b60c0d..2715af0 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -16,6 +16,8 @@
package android.net.http;
+import com.android.internal.util.HexDump;
+
import android.content.Context;
import android.os.Bundle;
import android.text.format.DateFormat;
@@ -285,7 +287,7 @@
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
- IntegralToString.appendByteAsHex(sb, b, true);
+ HexDump.appendByteAsHex(sb, b, true);
if (i+1 != bytes.length) {
sb.append(':');
}
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index 25ef8b5..6729347 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -20,6 +20,9 @@
import com.android.org.conscrypt.TrustManagerImpl;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
@@ -36,7 +39,11 @@
*/
public class X509TrustManagerExtensions {
- final TrustManagerImpl mDelegate;
+ private final TrustManagerImpl mDelegate;
+ // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used.
+ private final X509TrustManager mTrustManager;
+ private final Method mCheckServerTrusted;
+ private final Method mIsUserAddedCertificate;
/**
* Constructs a new X509TrustManagerExtensions wrapper.
@@ -47,10 +54,31 @@
public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException {
if (tm instanceof TrustManagerImpl) {
mDelegate = (TrustManagerImpl) tm;
- } else {
- mDelegate = null;
- throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
- " which is not a supported type of X509TrustManager");
+ mTrustManager = null;
+ mCheckServerTrusted = null;
+ mIsUserAddedCertificate = null;
+ return;
+ }
+ // Use duck typing if possible.
+ mDelegate = null;
+ mTrustManager = tm;
+ // Check that the hostname aware checkServerTrusted is present.
+ try {
+ mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted",
+ X509Certificate[].class,
+ String.class,
+ String.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Required method"
+ + " checkServerTrusted(X509Certificate[], String, String, String) missing");
+ }
+ // Check that isUserAddedCertificate is present.
+ try {
+ mIsUserAddedCertificate = tm.getClass().getMethod("isUserAddedCertificate",
+ X509Certificate.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ "Required method isUserAddedCertificate(X509Certificate) missing");
}
}
@@ -66,7 +94,24 @@
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
String host) throws CertificateException {
- return mDelegate.checkServerTrusted(chain, authType, host);
+ if (mDelegate != null) {
+ return mDelegate.checkServerTrusted(chain, authType, host);
+ } else {
+ try {
+ return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain,
+ authType, host);
+ } catch (IllegalAccessException e) {
+ throw new CertificateException("Failed to call checkServerTrusted", e);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof CertificateException) {
+ throw (CertificateException) e.getCause();
+ }
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ }
+ throw new CertificateException("checkServerTrusted failed", e.getCause());
+ }
+ }
}
/**
@@ -80,7 +125,21 @@
* otherwise.
*/
public boolean isUserAddedCertificate(X509Certificate cert) {
- return mDelegate.isUserAddedCertificate(cert);
+ if (mDelegate != null) {
+ return mDelegate.isUserAddedCertificate(cert);
+ } else {
+ try {
+ return (Boolean) mIsUserAddedCertificate.invoke(mTrustManager, cert);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Failed to call isUserAddedCertificate", e);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new RuntimeException("isUserAddedCertificate failed", e.getCause());
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index f55883a..7a1a6a2 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -35,4 +35,6 @@
long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason);
void exitIdle(String reason);
+ void downloadServiceActive(IBinder token);
+ void downloadServiceInactive();
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index b5bbbbb..c71d6cc 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -56,7 +56,6 @@
Bundle getUserRestrictions(int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
void setUserRestriction(String key, boolean value, int userId);
- void setSystemControlledUserRestriction(String key, boolean value, int userId);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
Bundle getApplicationRestrictions(in String packageName);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5852f5f..f2aea08 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -624,6 +624,32 @@
}
/**
+ * {@hide}
+ * This will be the new name for writeFileDescriptor, for consistency.
+ **/
+ public final void writeRawFileDescriptor(FileDescriptor val) {
+ nativeWriteFileDescriptor(mNativePtr, val);
+ }
+
+ /**
+ * {@hide}
+ * Write an array of FileDescriptor objects into the Parcel.
+ *
+ * @param value The array of objects to be written.
+ */
+ public final void writeRawFileDescriptorArray(FileDescriptor[] value) {
+ if (value != null) {
+ int N = value.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeRawFileDescriptor(value[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
* Write a byte value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
@@ -1700,6 +1726,41 @@
return nativeReadFileDescriptor(mNativePtr);
}
+ /**
+ * {@hide}
+ * Read and return a new array of FileDescriptors from the parcel.
+ * @return the FileDescriptor array, or null if the array is null.
+ **/
+ public final FileDescriptor[] createRawFileDescriptorArray() {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ FileDescriptor[] f = new FileDescriptor[N];
+ for (int i = 0; i < N; i++) {
+ f[i] = readRawFileDescriptor();
+ }
+ return f;
+ }
+
+ /**
+ * {@hide}
+ * Read an array of FileDescriptors from a parcel.
+ * The passed array must be exactly the length of the array in the parcel.
+ * @return the FileDescriptor array, or null if the array is null.
+ **/
+ public final void readRawFileDescriptorArray(FileDescriptor[] val) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = readRawFileDescriptor();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
+
/*package*/ static native FileDescriptor openFileDescriptor(String file,
int mode) throws FileNotFoundException;
/*package*/ static native FileDescriptor dupFileDescriptor(FileDescriptor orig)
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4ac361d0..54bfca3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -632,9 +632,6 @@
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
- if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) {
- argsForZygote.add("--enable-jit");
- }
if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
argsForZygote.add("--generate-debug-info");
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 4535572..407ff08 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -44,11 +44,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import org.apache.harmony.security.asn1.BerInputStream;
-import org.apache.harmony.security.pkcs7.ContentInfo;
-import org.apache.harmony.security.pkcs7.SignedData;
-import org.apache.harmony.security.pkcs7.SignerInfo;
-import org.apache.harmony.security.x509.Certificate;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
/**
* RecoverySystem contains methods for interacting with the Android
@@ -150,14 +147,13 @@
ProgressListener listener,
File deviceCertsZipFile)
throws IOException, GeneralSecurityException {
- long fileLen = packageFile.length();
+ final long fileLen = packageFile.length();
- RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
+ final RandomAccessFile raf = new RandomAccessFile(packageFile, "r");
try {
- int lastPercent = 0;
- long lastPublishTime = System.currentTimeMillis();
+ final long startTimeMillis = System.currentTimeMillis();
if (listener != null) {
- listener.onProgress(lastPercent);
+ listener.onProgress(0);
}
raf.seek(fileLen - 6);
@@ -168,8 +164,8 @@
throw new SignatureException("no signature in file (no footer)");
}
- int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
- int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);
+ final int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8);
+ final int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8);
byte[] eocd = new byte[commentSize + 22];
raf.seek(fileLen - (commentSize + 22));
@@ -189,51 +185,30 @@
}
}
- // The following code is largely copied from
- // JarUtils.verifySignature(). We could just *call* that
- // method here if that function didn't read the entire
- // input (ie, the whole OTA package) into memory just to
- // compute its message digest.
+ // Parse the signature
+ PKCS7 block =
+ new PKCS7(new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
- BerInputStream bis = new BerInputStream(
- new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart));
- ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis);
- SignedData signedData = info.getSignedData();
- if (signedData == null) {
- throw new IOException("signedData is null");
- }
- List<Certificate> encCerts = signedData.getCertificates();
- if (encCerts.isEmpty()) {
- throw new IOException("encCerts is empty");
- }
// Take the first certificate from the signature (packages
// should contain only one).
- Iterator<Certificate> it = encCerts.iterator();
- X509Certificate cert = null;
- if (it.hasNext()) {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- InputStream is = new ByteArrayInputStream(it.next().getEncoded());
- cert = (X509Certificate) cf.generateCertificate(is);
- } else {
+ X509Certificate[] certificates = block.getCertificates();
+ if (certificates == null || certificates.length == 0) {
throw new SignatureException("signature contains no certificates");
}
+ X509Certificate cert = certificates[0];
+ PublicKey signatureKey = cert.getPublicKey();
- List<SignerInfo> sigInfos = signedData.getSignerInfos();
- SignerInfo sigInfo;
- if (!sigInfos.isEmpty()) {
- sigInfo = (SignerInfo)sigInfos.get(0);
- } else {
- throw new IOException("no signer infos!");
+ SignerInfo[] signerInfos = block.getSignerInfos();
+ if (signerInfos == null || signerInfos.length == 0) {
+ throw new SignatureException("signature contains no signedData");
}
+ SignerInfo signerInfo = signerInfos[0];
// Check that the public key of the certificate contained
// in the package equals one of our trusted public keys.
-
+ boolean verified = false;
HashSet<X509Certificate> trusted = getTrustedCerts(
deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile);
-
- PublicKey signatureKey = cert.getPublicKey();
- boolean verified = false;
for (X509Certificate c : trusted) {
if (c.getPublicKey().equals(signatureKey)) {
verified = true;
@@ -246,61 +221,54 @@
// The signature cert matches a trusted key. Now verify that
// the digest in the cert matches the actual file data.
-
- // The verifier in recovery only handles SHA1withRSA and
- // SHA256withRSA signatures. SignApk chooses which to use
- // based on the signature algorithm of the cert:
- //
- // "SHA256withRSA" cert -> "SHA256withRSA" signature
- // "SHA1withRSA" cert -> "SHA1withRSA" signature
- // "MD5withRSA" cert -> "SHA1withRSA" signature (for backwards compatibility)
- // any other cert -> SignApk fails
- //
- // Here we ignore whatever the cert says, and instead use
- // whatever algorithm is used by the signature.
-
- String da = sigInfo.getDigestAlgorithm();
- String dea = sigInfo.getDigestEncryptionAlgorithm();
- String alg = null;
- if (da == null || dea == null) {
- // fall back to the cert algorithm if the sig one
- // doesn't look right.
- alg = cert.getSigAlgName();
- } else {
- alg = da + "with" + dea;
- }
- Signature sig = Signature.getInstance(alg);
- sig.initVerify(cert);
-
- // The signature covers all of the OTA package except the
- // archive comment and its 2-byte length.
- long toRead = fileLen - commentSize - 2;
- long soFar = 0;
raf.seek(0);
- byte[] buffer = new byte[4096];
- boolean interrupted = false;
- while (soFar < toRead) {
- interrupted = Thread.interrupted();
- if (interrupted) break;
- int size = buffer.length;
- if (soFar + size > toRead) {
- size = (int)(toRead - soFar);
- }
- int read = raf.read(buffer, 0, size);
- sig.update(buffer, 0, read);
- soFar += read;
+ final ProgressListener listenerForInner = listener;
+ SignerInfo verifyResult = block.verify(signerInfo, new InputStream() {
+ // The signature covers all of the OTA package except the
+ // archive comment and its 2-byte length.
+ long toRead = fileLen - commentSize - 2;
+ long soFar = 0;
- if (listener != null) {
- long now = System.currentTimeMillis();
- int p = (int)(soFar * 100 / toRead);
- if (p > lastPercent &&
- now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
- lastPercent = p;
- lastPublishTime = now;
- listener.onProgress(lastPercent);
- }
+ int lastPercent = 0;
+ long lastPublishTime = startTimeMillis;
+
+ @Override
+ public int read() throws IOException {
+ throw new UnsupportedOperationException();
}
- }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (soFar >= toRead) {
+ return -1;
+ }
+ if (Thread.currentThread().isInterrupted()) {
+ return -1;
+ }
+
+ int size = len;
+ if (soFar + size > toRead) {
+ size = (int)(toRead - soFar);
+ }
+ int read = raf.read(b, off, size);
+ soFar += read;
+
+ if (listenerForInner != null) {
+ long now = System.currentTimeMillis();
+ int p = (int)(soFar * 100 / toRead);
+ if (p > lastPercent &&
+ now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
+ lastPercent = p;
+ lastPublishTime = now;
+ listenerForInner.onProgress(lastPercent);
+ }
+ }
+
+ return read;
+ }
+ });
+
+ final boolean interrupted = Thread.interrupted();
if (listener != null) {
listener.onProgress(100);
}
@@ -309,7 +277,7 @@
throw new SignatureException("verification was interrupted");
}
- if (!sig.verify(sigInfo.getEncryptedDigest())) {
+ if (verifyResult == null) {
throw new SignatureException("signature digest verification failed");
}
} finally {
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index cad482b..6f12b62 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -48,19 +48,35 @@
private FastPrintWriter mErrPrintWriter;
private InputStream mInputStream;
- public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) {
+ public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, int firstArgPos) {
mTarget = target;
mIn = in;
mOut = out;
mErr = err;
mArgs = args;
- mResultReceiver = resultReceiver;
- mCmd = args != null && args.length > 0 ? args[0] : null;
- mArgPos = 1;
+ mResultReceiver = null;
+ mCmd = null;
+ mArgPos = firstArgPos;
mCurArgData = null;
mOutPrintWriter = null;
mErrPrintWriter = null;
+ }
+
+ public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ResultReceiver resultReceiver) {
+ String cmd;
+ int start;
+ if (args != null && args.length > 0) {
+ cmd = args[0];
+ start = 1;
+ } else {
+ cmd = null;
+ start = 0;
+ }
+ init(target, in, out, err, args, start);
+ mCmd = cmd;
+ mResultReceiver = resultReceiver;
if (DEBUG) Slog.d(TAG, "Starting command " + mCmd + " on " + mTarget);
int res = -1;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 95da438..f946ca7 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -256,6 +256,23 @@
}
}
+ /** @hide */
+ public static int parseUserArg(String arg) {
+ int userId;
+ if ("all".equals(arg)) {
+ userId = UserHandle.USER_ALL;
+ } else if ("current".equals(arg) || "cur".equals(arg)) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ try {
+ userId = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Bad user number: " + arg);
+ }
+ }
+ return userId;
+ }
+
/**
* Returns the user id of the current process
* @return user id of the current process
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 9178ec6..26c7a1e 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -15,6 +15,9 @@
*/
package android.os;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* @hide Only for use within the system server.
*/
@@ -31,32 +34,18 @@
}
/**
- * Lock that must be held when calling certain methods in this class.
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
+ * to set per-user as well as global user restrictions.
*
- * This is used to avoid dead lock between
- * {@link com.android.server.pm.UserManagerService} and
- * {@link com.android.server.devicepolicy.DevicePolicyManagerService}. This lock should not
- * be newly taken while holding the DPMS lock, which would cause a dead lock. Take this
- * lock first before taking the DPMS lock to avoid that.
+ * @param userId target user id for the local restrictions.
+ * @param localRestrictions per-user restrictions.
+ * Caller must not change it once passed to this method.
+ * @param globalRestrictions global restrictions set by DO. Must be null when PO changed user
+ * restrictions, in which case global restrictions won't change.
+ * Caller must not change it once passed to this method.
*/
- public abstract Object getUserRestrictionsLock();
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to get
- * {@link com.android.server.pm.UserManagerService} to update effective user restrictions.
- *
- * Must be called while taking the {@link #getUserRestrictionsLock()} lock.
- */
- public abstract void updateEffectiveUserRestrictionsLR(int userId);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to get
- * {@link com.android.server.pm.UserManagerService} to update effective user restrictions.
- *
- * Must be called while taking the {@link #getUserRestrictionsLock()} lock.
- */
- public abstract void updateEffectiveUserRestrictionsForAllUsersLR();
-
+ public abstract void setDevicePolicyUserRestrictions(int userId,
+ @NonNull Bundle localRestrictions, @Nullable Bundle globalRestrictions);
/**
* Returns the "base" user restrictions.
*
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 5d45ade..c6510f0 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -21,8 +21,12 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.RemoteException;
+import java.io.FileDescriptor;
+
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
* file. In particular, the ordering of the methods below must match the
@@ -1198,14 +1202,14 @@
}
@Override
- public void createNewUserDir(int userHandle, String path) throws RemoteException {
+ public void createUserKey(int userId, int serialNumber) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userHandle);
- _data.writeString(path);
- mRemote.transact(Stub.TRANSACTION_createNewUserDir, _data, _reply, 0);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
@@ -1214,13 +1218,13 @@
}
@Override
- public void deleteUserKey(int userHandle) throws RemoteException {
+ public void destroyUserKey(int userId) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userHandle);
- mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_destroyUserKey, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
@@ -1229,13 +1233,46 @@
}
@Override
- public boolean isPerUserEncryptionEnabled() throws RemoteException {
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ _data.writeByteArray(token);
+ mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void lockUserKey(int userId) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_lockUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public boolean isUserKeyUnlocked(int userId) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- mRemote.transact(Stub.TRANSACTION_isPerUserEncryptionEnabled, _data, _reply, 0);
+ _data.writeInt(userId);
+ mRemote.transact(Stub.TRANSACTION_isUserKeyUnlocked, _data, _reply, 0);
_reply.readException();
_result = 0 != _reply.readInt();
} finally {
@@ -1244,6 +1281,43 @@
}
return _result;
}
+
+ @Override
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volumeUuid);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ ParcelFileDescriptor _result = null;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(name);
+ mRemote.transact(Stub.TRANSACTION_mountAppFuse, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.<ParcelFileDescriptor>readParcelable(
+ ClassLoader.getSystemClassLoader());
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1359,13 +1433,18 @@
static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
- static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62;
+ static final int TRANSACTION_createUserKey = IBinder.FIRST_CALL_TRANSACTION + 61;
+ static final int TRANSACTION_destroyUserKey = IBinder.FIRST_CALL_TRANSACTION + 62;
- static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+ static final int TRANSACTION_unlockUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+ static final int TRANSACTION_lockUserKey = IBinder.FIRST_CALL_TRANSACTION + 64;
+ static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
- static final int TRANSACTION_isPerUserEncryptionEnabled = IBinder.FIRST_CALL_TRANSACTION + 64;
+ static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
- static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 65;
+ static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
+
+ static final int TRANSACTION_mountAppFuse = IBinder.FIRST_CALL_TRANSACTION + 69;
/**
* Cast an IBinder object into an IMountService interface, generating a
@@ -1929,26 +2008,60 @@
reply.writeNoException();
return true;
}
- case TRANSACTION_createNewUserDir: {
+ case TRANSACTION_createUserKey: {
data.enforceInterface(DESCRIPTOR);
- int userHandle = data.readInt();
- String path = data.readString();
- createNewUserDir(userHandle, path);
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ createUserKey(userId, serialNumber);
reply.writeNoException();
return true;
}
- case TRANSACTION_deleteUserKey: {
+ case TRANSACTION_destroyUserKey: {
data.enforceInterface(DESCRIPTOR);
- int userHandle = data.readInt();
- deleteUserKey(userHandle);
+ int userId = data.readInt();
+ destroyUserKey(userId);
reply.writeNoException();
return true;
}
- case TRANSACTION_isPerUserEncryptionEnabled: {
+ case TRANSACTION_unlockUserKey: {
data.enforceInterface(DESCRIPTOR);
- boolean result = isPerUserEncryptionEnabled();
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ byte[] token = data.createByteArray();
+ unlockUserKey(userId, serialNumber, token);
reply.writeNoException();
- reply.writeInt((result ? 1 : 0));
+ return true;
+ }
+ case TRANSACTION_lockUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ lockUserKey(userId);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_isUserKeyUnlocked: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ boolean result = isUserKeyUnlocked(userId);
+ reply.writeNoException();
+ reply.writeInt(result ? 1 : 0);
+ return true;
+ }
+ case TRANSACTION_prepareUserStorage: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = data.readString();
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ prepareUserStorage(volumeUuid, userId, serialNumber);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_mountAppFuse: {
+ data.enforceInterface(DESCRIPTOR);
+ String name = data.readString();
+ ParcelFileDescriptor fd = mountAppFuse(name);
+ reply.writeNoException();
+ reply.writeParcelable(fd, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
}
}
@@ -2263,24 +2376,15 @@
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
- /**
- * Creates the user data directory, possibly encrypted
- * @param userHandle Handle of the user whose directory we are creating
- * @param path Path at which to create the directory.
- */
- public void createNewUserDir(int userHandle, String path)
- throws RemoteException;
+ public void createUserKey(int userId, int serialNumber) throws RemoteException;
+ public void destroyUserKey(int userId) throws RemoteException;
- /**
- * Securely delete the user's encryption key
- * @param userHandle Handle of the user whose key we are deleting
- */
- public void deleteUserKey(int userHandle)
- throws RemoteException;
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+ public void lockUserKey(int userId) throws RemoteException;
+ public boolean isUserKeyUnlocked(int userId) throws RemoteException;
- /**
- * Returns whether the current encryption type is per user.
- */
- public boolean isPerUserEncryptionEnabled()
- throws RemoteException;
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ throws RemoteException;
+
+ public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2ca0b20..db12564 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -30,8 +30,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -77,6 +79,8 @@
public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
/** {@hide} */
public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+ /** {@hide} */
+ public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
@@ -85,6 +89,8 @@
/** {@hide} */
public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+ /** {@hide} */
+ public static final int DEBUG_EMULATE_FBE = 1 << 1;
/** {@hide} */
public static final int FLAG_FOR_WRITE = 1 << 0;
@@ -287,7 +293,7 @@
/**
* Constructs a StorageManager object through which an application can
* can communicate with the systems mount service.
- *
+ *
* @param tgtLooper The {@link android.os.Looper} which events will be received on.
*
* <p>Applications can get instance of this class by calling
@@ -403,7 +409,7 @@
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can attempt to mount any other
* application's OBB that shares its UID.
- *
+ *
* @param rawPath the path to the OBB file
* @param key secret used to encrypt the OBB; may be <code>null</code> if no
* encryption was used on the OBB.
@@ -441,7 +447,7 @@
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
* <p>
- *
+ *
* @param rawPath path to the OBB file
* @param force whether to kill any programs using this in order to unmount
* it
@@ -465,7 +471,7 @@
/**
* Check whether an Opaque Binary Blob (OBB) is mounted or not.
- *
+ *
* @param rawPath path to OBB image
* @return true if OBB is mounted; false if not mounted or on error
*/
@@ -485,7 +491,7 @@
* Check the mounted path of an Opaque Binary Blob (OBB) file. This will
* give you the path to where you can obtain access to the internals of the
* OBB.
- *
+ *
* @param rawPath path to OBB image
* @return absolute path to mounted OBB image data or <code>null</code> if
* not mounted or exception encountered trying to read status
@@ -960,33 +966,66 @@
}
/** {@hide} */
- public void createNewUserDir(int userHandle, File path) {
+ public void createUserKey(int userId, int serialNumber) {
try {
- mMountService.createNewUserDir(userHandle, path.getAbsolutePath());
+ mMountService.createUserKey(userId, serialNumber);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public void deleteUserKey(int userHandle) {
+ public void destroyUserKey(int userId) {
try {
- mMountService.deleteUserKey(userHandle);
+ mMountService.destroyUserKey(userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public boolean isPerUserEncryptionEnabled() {
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) {
try {
- return mMountService.isPerUserEncryptionEnabled();
+ mMountService.unlockUserKey(userId, serialNumber, token);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
+ public void lockUserKey(int userId) {
+ try {
+ mMountService.lockUserKey(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ try {
+ mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public boolean isUserKeyUnlocked(int userId) {
+ try {
+ return mMountService.isUserKeyUnlocked(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public static boolean isFileBasedEncryptionEnabled() {
+ return "file".equals(SystemProperties.get("ro.crypto.type", "none"))
+ || SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
+ }
+
+ /** {@hide} */
public static File maybeTranslateEmulatedPathToInternal(File path) {
final IMountService mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
@@ -1007,6 +1046,15 @@
return path;
}
+ /** {@hide} */
+ public ParcelFileDescriptor mountAppFuse(String name) {
+ try {
+ return mMountService.mountAppFuse(name);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/// Consts to match the password types in cryptfs.h
/** @hide */
public static final int CRYPT_TYPE_PASSWORD = 0;
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index c368e5a..5997515 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -435,7 +435,7 @@
return null;
}
- final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(uri);
intent.putExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, true);
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index db04c71..3e496b6 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -23,14 +23,15 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnKeyListener;
+import android.view.ViewGroup;
import android.widget.ListView;
/**
@@ -179,6 +180,27 @@
}
@Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ TypedArray a = getActivity().obtainStyledAttributes(null,
+ com.android.internal.R.styleable.PreferenceFragment,
+ com.android.internal.R.attr.preferenceFragmentStyle,
+ 0);
+
+ ListView lv = (ListView) view.findViewById(android.R.id.list);
+ if (lv != null) {
+ Drawable divider =
+ a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider);
+ if (divider != null) {
+ lv.setDivider(divider);
+ }
+ }
+
+ a.recycle();
+ }
+
+ @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 9c3cefc..aa8674e 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -122,7 +122,7 @@
protected void onBindView(View view) {
super.onBindView(view);
- View checkableView = view.findViewById(com.android.internal.R.id.switchWidget);
+ View checkableView = view.findViewById(com.android.internal.R.id.switch_widget);
if (checkableView != null && checkableView instanceof Checkable) {
if (checkableView instanceof Switch) {
final Switch switchView = (Switch) checkableView;
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 90d30d6..2afbb99 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -60,7 +60,7 @@
private Margins mMinMargins;
private int mColorMode;
- private int mDuplexMode = DUPLEX_MODE_NONE;
+ private int mDuplexMode;
PrintAttributes() {
/* hide constructor */
@@ -403,7 +403,7 @@
mResolution = null;
mMinMargins = null;
mColorMode = 0;
- mDuplexMode = DUPLEX_MODE_NONE;
+ mDuplexMode = 0;
}
/**
@@ -1427,10 +1427,6 @@
/**
* Creates a new {@link PrintAttributes} instance.
- * <p>
- * If you do not specify a duplex mode, the default
- * {@link #DUPLEX_MODE_NONE} will be used.
- * </p>
*
* @return The new instance.
*/
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e3694e8..94a2bea 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -394,6 +394,14 @@
Uri.withAppendedPath(AUTHORITY_URI, "directories");
/**
+ * The content:// style URI for enterprise Directory table. Requests to this URI can be
+ * performed on the UI thread because they are always unblocking.
+ *
+ */
+ public static final Uri CORP_CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "directories_corp");
+
+ /**
* The MIME-type of {@link #CONTENT_URI} providing a directory of
* contact directories.
*/
@@ -417,6 +425,22 @@
public static final long LOCAL_INVISIBLE = 1;
/**
+ * _ID of the work profile default directory, which represents locally stored contacts.
+ *
+ * @hide
+ */
+ public static final long ENTERPRISE_DEFAULT = Directory.ENTERPRISE_DIRECTORY_ID_BASE
+ + DEFAULT;
+
+ /**
+ * _ID of the work profile directory that represents locally stored invisible contacts.
+ *
+ * @hide
+ */
+ public static final long ENTERPRISE_LOCAL_INVISIBLE = Directory.ENTERPRISE_DIRECTORY_ID_BASE
+ + LOCAL_INVISIBLE;
+
+ /**
* The name of the package that owns this directory. Contacts Provider
* fill it in with the name of the package containing the directory provider.
* If the package is later uninstalled, the directories it owns are
@@ -472,6 +496,15 @@
public static final String ACCOUNT_NAME = "accountName";
/**
+ * Mimimal ID for corp directory returned from
+ * {@link Directory#CORP_CONTENT_URI}.
+ *
+ * @hide
+ */
+ // slightly smaller than 2 ** 30
+ public static final long ENTERPRISE_DIRECTORY_ID_BASE = 1000000000;
+
+ /**
* One of {@link #EXPORT_SUPPORT_NONE}, {@link #EXPORT_SUPPORT_ANY_ACCOUNT},
* {@link #EXPORT_SUPPORT_SAME_ACCOUNT_ONLY}. This is the expectation the
* directory has for data exported from it. Clients must obey this setting.
@@ -555,6 +588,24 @@
public static final int PHOTO_SUPPORT_FULL = 3;
/**
+ * Return TRUE if it is a remote stored directory.
+ */
+ public static boolean isRemoteDirectory(long directoryId) {
+ return directoryId != Directory.DEFAULT
+ && directoryId != Directory.LOCAL_INVISIBLE
+ && directoryId != Directory.ENTERPRISE_DEFAULT
+ && directoryId != Directory.ENTERPRISE_LOCAL_INVISIBLE;
+ }
+
+ /**
+ * Return TRUE if a directory ID is from the contacts provider on the enterprise profile.
+ *
+ */
+ public static boolean isEnterpriseDirectoryId(long directoryId) {
+ return directoryId >= ENTERPRISE_DIRECTORY_ID_BASE;
+ }
+
+ /**
* Notifies the system of a change in the list of directories handled by
* a particular directory provider. The Contacts provider will turn around
* and send a query to the directory provider for the full list of directories,
@@ -1589,6 +1640,14 @@
CONTENT_URI, "filter");
/**
+ * It supports the same semantics as {@link #CONTENT_FILTER_URI} and returns the same
+ * columns. If there is a corp profile linked to the current profile, it will query corp
+ * profile, otherwise it will return null.
+ */
+ public static final Uri CORP_CONTENT_FILTER_URI = Uri.withAppendedPath(
+ CORP_CONTENT_URI, "filter");
+
+ /**
* The content:// style URI for this table joined with useful data from
* {@link ContactsContract.Data}, filtered to include only starred contacts
* and the most frequently contacted contacts.
@@ -8301,10 +8360,15 @@
* @hide
*/
public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
- Intent originalIntent) {
+ long directoryId, Intent originalIntent) {
final Intent intent = new Intent(ACTION_QUICK_CONTACT);
// Rebuild the URI from a lookup key and a contact ID.
- intent.setData(Contacts.getLookupUri(contactId, lookupKey));
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ if (uri != null && directoryId != Directory.DEFAULT) {
+ uri = uri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
+ }
+ intent.setData(uri);
// Copy flags and always set NEW_TASK because it won't have a parent activity.
intent.setFlags(originalIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 059a603..36ef52d 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -91,6 +91,10 @@
final List<String> pathSegments = uri.getPathSegments();
final long contactId = ContentUris.parseId(uri);
final String lookupKey = pathSegments.get(2);
+ final String directoryIdStr = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ final long directoryId = (directoryIdStr == null)
+ ? ContactsContract.Directory.ENTERPRISE_DIRECTORY_ID_BASE
+ : Long.parseLong(directoryIdStr);
// See if it has a corp lookupkey.
if (TextUtils.isEmpty(lookupKey)
@@ -99,14 +103,24 @@
return false; // It's not a corp lookup key.
}
+ if (!ContactsContract.Contacts.isEnterpriseContactId(contactId)) {
+ throw new IllegalArgumentException("Invalid enterprise contact id: " + contactId);
+ }
+ if (!ContactsContract.Directory.isEnterpriseDirectoryId(directoryId)) {
+ throw new IllegalArgumentException("Invalid enterprise directory id: " + directoryId);
+ }
+
// Launch Quick Contact on the managed profile, if the policy allows.
final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
final String actualLookupKey = lookupKey.substring(
ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX.length());
final long actualContactId =
(contactId - ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE);
+ final long actualDirectoryId = (directoryId
+ - ContactsContract.Directory.ENTERPRISE_DIRECTORY_ID_BASE);
- dpm.startManagedQuickContact(actualLookupKey, actualContactId, originalIntent);
+ dpm.startManagedQuickContact(actualLookupKey, actualContactId, actualDirectoryId,
+ originalIntent);
return true;
}
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 159ca01..8ae899f 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -125,8 +125,7 @@
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
/** {@hide} */
- public static final String
- ACTION_BROWSE_DOCUMENT_ROOT = "android.provider.action.BROWSE_DOCUMENT_ROOT";
+ public static final String ACTION_BROWSE = "android.provider.action.BROWSE";
/** {@hide} */
public static final String
@@ -234,6 +233,7 @@
* @see #FLAG_SUPPORTS_TYPED_DOCUMENT
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
+ * @see #FLAG_VIRTUAL_DOCUMENT
*/
public static final String COLUMN_FLAGS = "flags";
@@ -357,6 +357,17 @@
public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 1 << 9;
/**
+ * Flag indicating that a document is virtual, and doesn't have byte
+ * representation in the MIME type specified as {@link #COLUMN_MIME_TYPE}.
+ *
+ * @see #COLUMN_FLAGS
+ * @see #COLUMN_MIME_TYPE
+ * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
+ * android.os.CancellationSignal)
+ */
+ public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 10;
+
+ /**
* Flag indicating that document titles should be hidden when viewing
* this directory in a larger format grid. For example, a directory
* containing only images may want the image thumbnails to speak for
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 37f2c04..f560f8a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6285,7 +6285,10 @@
public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
/**
- * Whether the device has been provisioned (0 = false, 1 = true)
+ * Whether the device has been provisioned (0 = false, 1 = true).
+ * <p>On a multiuser device with a separate system user, the screen may be locked
+ * as soon as this is set to true and further activities cannot be launched on the
+ * system user unless they are marked to show over keyguard.
*/
public static final String DEVICE_PROVISIONED = "device_provisioned";
@@ -7755,6 +7758,7 @@
AUTO_TIME_ZONE,
POWER_SOUNDS_ENABLED,
DOCK_SOUNDS_ENABLED,
+ CHARGING_SOUNDS_ENABLED,
USB_MASS_STORAGE_ENABLED,
ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 9bf344a..48359d47 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -30,6 +30,9 @@
* @hide
*/
public final class ApplicationConfig {
+ private static ApplicationConfig sInstance;
+ private static Object sLock = new Object();
+
private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
private NetworkSecurityConfig mDefaultConfig;
private X509TrustManager mTrustManager;
@@ -129,4 +132,30 @@
mInitialized = true;
}
}
+
+ public static void setDefaultInstance(ApplicationConfig config) {
+ synchronized (sLock) {
+ sInstance = config;
+ }
+ }
+
+ public static ApplicationConfig getDefaultInstance() {
+ synchronized (sLock) {
+ return sInstance;
+ }
+ }
+
+ /** @hide */
+ public static ApplicationConfig getPlatformDefault() {
+ return new ApplicationConfig(new ConfigSource() {
+ @Override
+ public NetworkSecurityConfig getDefaultConfig() {
+ return NetworkSecurityConfig.DEFAULT;
+ }
+ @Override
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ return null;
+ }
+ });
+ }
}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
new file mode 100644
index 0000000..1973ef1
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -0,0 +1,65 @@
+/*
+ * 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.security.net.config;
+
+import android.util.ArraySet;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.Set;
+
+/**
+ * {@link CertificateSource} which provides certificates from trusted certificate entries of a
+ * {@link KeyStore}.
+ */
+class KeyStoreCertificateSource implements CertificateSource {
+ private final Object mLock = new Object();
+ private final KeyStore mKeyStore;
+ private Set<X509Certificate> mCertificates;
+
+ public KeyStoreCertificateSource(KeyStore ks) {
+ mKeyStore = ks;
+ }
+
+ @Override
+ public Set<X509Certificate> getCertificates() {
+ synchronized (mLock) {
+ if (mCertificates != null) {
+ return mCertificates;
+ }
+ try {
+ Set<X509Certificate> certificates = new ArraySet<>(mKeyStore.size());
+ for (Enumeration<String> en = mKeyStore.aliases(); en.hasMoreElements();) {
+ String alias = en.nextElement();
+ if (!mKeyStore.isCertificateEntry(alias)) {
+ continue;
+ }
+ X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
+ if (cert != null) {
+ certificates.add(cert);
+ }
+ }
+ mCertificates = certificates;
+ return mCertificates;
+ } catch (KeyStoreException e) {
+ throw new RuntimeException("Failed to load certificates from KeyStore", e);
+ }
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/KeyStoreConfigSource.java b/core/java/android/security/net/config/KeyStoreConfigSource.java
new file mode 100644
index 0000000..8d4f098
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreConfigSource.java
@@ -0,0 +1,49 @@
+/*
+ * 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.security.net.config;
+
+import android.util.Pair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.Set;
+
+/**
+ * {@link ConfigSource} with a single default config based on a {@link KeyStore} and no per domain
+ * configs.
+ */
+class KeyStoreConfigSource implements ConfigSource {
+ private final NetworkSecurityConfig mConfig;
+
+ public KeyStoreConfigSource(KeyStore ks) {
+ mConfig = new NetworkSecurityConfig.Builder()
+ .addCertificatesEntryRef(
+ // Use the KeyStore and do not override pins (of which there are none).
+ new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
+ .build();
+ }
+
+ @Override
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ return null;
+ }
+
+ @Override
+ public NetworkSecurityConfig getDefaultConfig() {
+ return mConfig;
+ }
+}
+
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 1c787b4..9eab80c 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -16,11 +16,14 @@
package android.security.net.config;
+import android.util.ArrayMap;
import android.util.ArraySet;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.net.ssl.X509TrustManager;
@@ -41,7 +44,7 @@
private final List<CertificatesEntryRef> mCertificatesEntryRefs;
private Set<TrustAnchor> mAnchors;
private final Object mAnchorsLock = new Object();
- private X509TrustManager mTrustManager;
+ private NetworkSecurityTrustManager mTrustManager;
private final Object mTrustManagerLock = new Object();
private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
@@ -57,12 +60,24 @@
if (mAnchors != null) {
return mAnchors;
}
- Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
+ // Merge trust anchors based on the X509Certificate.
+ // If we see the same certificate in two TrustAnchors, one with overridesPins and one
+ // without, the one with overridesPins wins.
+ Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
- anchors.addAll(ref.getTrustAnchors());
+ Set<TrustAnchor> anchors = ref.getTrustAnchors();
+ for (TrustAnchor anchor : anchors) {
+ if (anchor.overridesPins) {
+ anchorMap.put(anchor.certificate, anchor);
+ } else if (!anchorMap.containsKey(anchor.certificate)) {
+ anchorMap.put(anchor.certificate, anchor);
+ }
+ }
}
+ ArraySet<TrustAnchor> anchors = new ArraySet<TrustAnchor>(anchorMap.size());
+ anchors.addAll(anchorMap.values());
mAnchors = anchors;
- return anchors;
+ return mAnchors;
}
}
@@ -78,7 +93,7 @@
return mPins;
}
- public X509TrustManager getTrustManager() {
+ public NetworkSecurityTrustManager getTrustManager() {
synchronized(mTrustManagerLock) {
if (mTrustManager == null) {
mTrustManager = new NetworkSecurityTrustManager(this);
@@ -227,10 +242,14 @@
return Collections.<CertificatesEntryRef>emptyList();
}
- public boolean hasCertificateEntryRefs() {
+ public boolean hasCertificatesEntryRefs() {
return mCertificatesEntryRefs != null;
}
+ List<CertificatesEntryRef> getCertificatesEntryRefs() {
+ return mCertificatesEntryRefs;
+ }
+
public NetworkSecurityConfig build() {
boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
boolean hstsEnforced = getEffectiveHstsEnforced();
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
new file mode 100644
index 0000000..ac762ef
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -0,0 +1,80 @@
+/*
+ * 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.security.net.config;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import java.security.Security;
+import java.security.Provider;
+
+/** @hide */
+public final class NetworkSecurityConfigProvider extends Provider {
+ private static final String LOG_TAG = "NetworkSecurityConfig";
+ private static final String PREFIX =
+ NetworkSecurityConfigProvider.class.getPackage().getName() + ".";
+ public static final String META_DATA_NETWORK_SECURITY_CONFIG =
+ "android.security.net.config";
+ private static final boolean DBG = true;
+
+ public NetworkSecurityConfigProvider() {
+ // TODO: More clever name than this
+ super("AndroidNSSP", 1.0, "Android Network Security Policy Provider");
+ put("TrustManagerFactory.PKIX", PREFIX + "RootTrustManagerFactorySpi");
+ put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
+ }
+
+ public static void install(Context context) {
+ ApplicationInfo info = null;
+ // TODO: This lookup shouldn't be done in the app startup path, it should be done lazily.
+ try {
+ info = context.getPackageManager().getApplicationInfo(context.getPackageName(),
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Failed to look up ApplicationInfo", e);
+ }
+ int configResourceId = 0;
+ if (info != null && info.metaData != null) {
+ configResourceId = info.metaData.getInt(META_DATA_NETWORK_SECURITY_CONFIG);
+ }
+
+ ApplicationConfig config;
+ if (configResourceId != 0) {
+ boolean debugBuild = (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ if (DBG) {
+ Log.d(LOG_TAG, "Using Network Security Config from resource "
+ + context.getResources().getResourceEntryName(configResourceId)
+ + " debugBuild: " + debugBuild);
+ }
+ ConfigSource source = new XmlConfigSource(context, configResourceId, debugBuild);
+ config = new ApplicationConfig(source);
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
+ }
+ config = ApplicationConfig.getPlatformDefault();
+ }
+
+ ApplicationConfig.setDefaultInstance(config);
+ int pos = Security.insertProviderAt(new NetworkSecurityConfigProvider(), 1);
+ if (pos != 1) {
+ throw new RuntimeException("Failed to install provider as highest priority provider."
+ + " Provider was installed at position " + pos);
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index e69082d..7f5b3ca 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -71,9 +71,28 @@
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
- List<X509Certificate> trustedChain =
- mDelegate.checkServerTrusted(certs, authType, (String) null);
+ checkServerTrusted(certs, authType, null);
+ }
+
+ /**
+ * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+ * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+ * modify without modifying those callers.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+ String host) throws CertificateException {
+ List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host);
checkPins(trustedChain);
+ return trustedChain;
+ }
+
+ /**
+ * Check if the provided certificate is a user added certificate authority.
+ * This is required by android.net.http.X509TrustManagerExtensions.
+ */
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ // TODO: Figure out the right way to handle this, and if it is still even used.
+ return false;
}
private void checkPins(List<X509Certificate> chain) throws CertificateException {
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index 1338b9f..b87bf1f 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -18,6 +18,7 @@
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.List;
import javax.net.ssl.X509TrustManager;
@@ -61,10 +62,24 @@
config.getTrustManager().checkServerTrusted(certs, authType);
}
- public void checkServerTrusted(X509Certificate[] certs, String authType, String hostname)
- throws CertificateException {
+ /**
+ * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+ * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+ * modify without modifying those callers.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+ String hostname) throws CertificateException {
NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
- config.getTrustManager().checkServerTrusted(certs, authType);
+ return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
+ }
+
+ /**
+ * Check if the provided certificate is a user added certificate authority.
+ * This is required by android.net.http.X509TrustManagerExtensions.
+ */
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ // TODO: Figure out the right way to handle this, and if it is still even used.
+ return false;
}
@Override
diff --git a/core/java/android/security/net/config/RootTrustManagerFactorySpi.java b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
new file mode 100644
index 0000000..0a1fe88
--- /dev/null
+++ b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
@@ -0,0 +1,75 @@
+/*
+ * 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.security.net.config;
+
+import android.util.Pair;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Set;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManagerFactorySpi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** @hide */
+public class RootTrustManagerFactorySpi extends TrustManagerFactorySpi {
+ private ApplicationConfig mApplicationConfig;
+ private NetworkSecurityConfig mConfig;
+
+ @Override
+ public void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ if (!(spec instanceof ApplicationConfigParameters)) {
+ throw new InvalidAlgorithmParameterException("Unsupported spec: " + spec + ". Only "
+ + ApplicationConfigParameters.class.getName() + " supported");
+
+ }
+ mApplicationConfig = ((ApplicationConfigParameters) spec).config;
+ }
+
+ @Override
+ public void engineInit(KeyStore ks) throws KeyStoreException {
+ if (ks != null) {
+ mApplicationConfig = new ApplicationConfig(new KeyStoreConfigSource(ks));
+ } else {
+ mApplicationConfig = ApplicationConfig.getDefaultInstance();
+ }
+ }
+
+ @Override
+ public TrustManager[] engineGetTrustManagers() {
+ if (mApplicationConfig == null) {
+ throw new IllegalStateException("TrustManagerFactory not initialized");
+ }
+ return new TrustManager[] { mApplicationConfig.getTrustManager() };
+ }
+
+ @VisibleForTesting
+ public static final class ApplicationConfigParameters implements ManagerFactoryParameters {
+ public final ApplicationConfig config;
+ public ApplicationConfigParameters(ApplicationConfig config) {
+ this.config = config;
+ }
+ }
+}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index 6abfb66..1706e95 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -27,8 +27,13 @@
* @hide
*/
public class XmlConfigSource implements ConfigSource {
+ private static final int CONFIG_BASE = 0;
+ private static final int CONFIG_DOMAIN = 1;
+ private static final int CONFIG_DEBUG = 2;
+
private final Object mLock = new Object();
private final int mResourceId;
+ private final boolean mDebugBuild;
private boolean mInitialized;
private NetworkSecurityConfig mDefaultConfig;
@@ -36,8 +41,13 @@
private Context mContext;
public XmlConfigSource(Context context, int resourceId) {
+ this(context, resourceId, false);
+ }
+
+ public XmlConfigSource(Context context, int resourceId, boolean debugBuild) {
mResourceId = resourceId;
mContext = context;
+ mDebugBuild = debugBuild;
}
public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
@@ -50,6 +60,19 @@
return mDefaultConfig;
}
+ private static final String getConfigString(int configType) {
+ switch (configType) {
+ case CONFIG_BASE:
+ return "base-config";
+ case CONFIG_DOMAIN:
+ return "domain-config";
+ case CONFIG_DEBUG:
+ return "debug-overrides";
+ default:
+ throw new IllegalArgumentException("Unknown config type: " + configType);
+ }
+ }
+
private void ensureInitialized() {
synchronized (mLock) {
if (mInitialized) {
@@ -147,9 +170,11 @@
return new Domain(domain, includeSubdomains);
}
- private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser)
+ private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser,
+ boolean defaultOverridePins)
throws IOException, XmlPullParserException, ParserException {
- boolean overridePins = parser.getAttributeBooleanValue(null, "overridePins", false);
+ boolean overridePins =
+ parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
int sourceId = parser.getAttributeResourceValue(null, "src", -1);
String sourceString = parser.getAttributeValue(null, "src");
CertificateSource source = null;
@@ -171,14 +196,15 @@
return new CertificatesEntryRef(source, overridePins);
}
- private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser)
+ private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
+ boolean defaultOverridePins)
throws IOException, XmlPullParserException, ParserException {
int outerDepth = parser.getDepth();
List<CertificatesEntryRef> anchors = new ArrayList<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
String tagName = parser.getName();
if (tagName.equals("certificates")) {
- anchors.add(parseCertificatesEntry(parser));
+ anchors.add(parseCertificatesEntry(parser, defaultOverridePins));
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -188,7 +214,7 @@
private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry(
XmlResourceParser parser, Set<String> seenDomains,
- NetworkSecurityConfig.Builder parentBuilder, boolean baseConfig)
+ NetworkSecurityConfig.Builder parentBuilder, int configType)
throws IOException, XmlPullParserException, ParserException {
List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder();
@@ -196,6 +222,7 @@
Set<Domain> domains = new ArraySet<>();
boolean seenPinSet = false;
boolean seenTrustAnchors = false;
+ boolean defaultOverridePins = configType == CONFIG_DEBUG;
String configName = parser.getName();
int outerDepth = parser.getDepth();
// Add this builder now so that this builder occurs before any of its children. This
@@ -219,8 +246,9 @@
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
String tagName = parser.getName();
if ("domain".equals(tagName)) {
- if (baseConfig) {
- throw new ParserException(parser, "domain element not allowed in base-config");
+ if (configType != CONFIG_DOMAIN) {
+ throw new ParserException(parser,
+ "domain element not allowed in " + getConfigString(configType));
}
Domain domain = parseDomain(parser, seenDomains);
domains.add(domain);
@@ -229,12 +257,13 @@
throw new ParserException(parser,
"Multiple trust-anchor elements not allowed");
}
- builder.addCertificatesEntryRefs(parseTrustAnchors(parser));
+ builder.addCertificatesEntryRefs(
+ parseTrustAnchors(parser, defaultOverridePins));
seenTrustAnchors = true;
} else if ("pin-set".equals(tagName)) {
- if (baseConfig) {
+ if (configType != CONFIG_DOMAIN) {
throw new ParserException(parser,
- "pin-set element not allowed in base-config");
+ "pin-set element not allowed in " + getConfigString(configType));
}
if (seenPinSet) {
throw new ParserException(parser, "Multiple pin-set elements not allowed");
@@ -242,41 +271,68 @@
builder.setPinSet(parsePinSet(parser));
seenPinSet = true;
} else if ("domain-config".equals(tagName)) {
- if (baseConfig) {
+ if (configType != CONFIG_DOMAIN) {
throw new ParserException(parser,
- "Nested domain-config not allowed in base-config");
+ "Nested domain-config not allowed in " + getConfigString(configType));
}
- builders.addAll(parseConfigEntry(parser, seenDomains, builder, false));
+ builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType));
} else {
XmlUtils.skipCurrentTag(parser);
}
}
- if (!baseConfig && domains.isEmpty()) {
+ if (configType == CONFIG_DOMAIN && domains.isEmpty()) {
throw new ParserException(parser, "No domain elements in domain-config");
}
return builders;
}
+ private void addDebugAnchorsIfNeeded(NetworkSecurityConfig.Builder debugConfigBuilder,
+ NetworkSecurityConfig.Builder builder) {
+ if (debugConfigBuilder == null || !debugConfigBuilder.hasCertificatesEntryRefs()) {
+ return;
+ }
+ // Don't add trust anchors if not already present, the builder will inherit the anchors
+ // from its parent, and that's where the trust anchors should be added.
+ if (!builder.hasCertificatesEntryRefs()) {
+ return;
+ }
+
+ builder.addCertificatesEntryRefs(debugConfigBuilder.getCertificatesEntryRefs());
+ }
+
private void parseNetworkSecurityConfig(XmlResourceParser parser)
throws IOException, XmlPullParserException, ParserException {
Set<String> seenDomains = new ArraySet<>();
List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
NetworkSecurityConfig.Builder baseConfigBuilder = null;
+ NetworkSecurityConfig.Builder debugConfigBuilder = null;
boolean seenDebugOverrides = false;
boolean seenBaseConfig = false;
XmlUtils.beginDocument(parser, "network-security-config");
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- // TODO: support debug-override.
if ("base-config".equals(parser.getName())) {
if (seenBaseConfig) {
throw new ParserException(parser, "Only one base-config allowed");
}
seenBaseConfig = true;
- baseConfigBuilder = parseConfigEntry(parser, seenDomains, null, true).get(0).first;
+ baseConfigBuilder =
+ parseConfigEntry(parser, seenDomains, null, CONFIG_BASE).get(0).first;
} else if ("domain-config".equals(parser.getName())) {
- builders.addAll(parseConfigEntry(parser, seenDomains, baseConfigBuilder, false));
+ builders.addAll(
+ parseConfigEntry(parser, seenDomains, baseConfigBuilder, CONFIG_DOMAIN));
+ } else if ("debug-overrides".equals(parser.getName())) {
+ if (seenDebugOverrides) {
+ throw new ParserException(parser, "Only one debug-overrides allowed");
+ }
+ if (mDebugBuild) {
+ debugConfigBuilder =
+ parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ seenDebugOverrides = true;
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -286,8 +342,10 @@
// there. If there is no base config use the platform default.
NetworkSecurityConfig.Builder platformDefaultBuilder =
NetworkSecurityConfig.getDefaultBuilder();
+ addDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder);
if (baseConfigBuilder != null) {
baseConfigBuilder.setParent(platformDefaultBuilder);
+ addDebugAnchorsIfNeeded(debugConfigBuilder, baseConfigBuilder);
} else {
baseConfigBuilder = platformDefaultBuilder;
}
@@ -306,6 +364,7 @@
if (builder.getParent() == null) {
builder.setParent(baseConfigBuilder);
}
+ addDebugAnchorsIfNeeded(debugConfigBuilder, builder);
NetworkSecurityConfig config = builder.build();
for (Domain domain : domains) {
configs.add(new Pair<>(domain, config));
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7e7b5fc..ee97e8e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -108,6 +108,11 @@
* This does not change the interruption filter, only the effects. **/
public static final int HINT_HOST_DISABLE_EFFECTS = 1;
+ public static final int SUPPRESSED_EFFECT_LIGHTS =
+ NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+ public static final int SUPPRESSED_EFFECT_PEEK =
+ NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+
/**
* The full trim of the StatusBarNotification including all its features.
*
@@ -822,6 +827,7 @@
private boolean mIsAmbient;
private boolean mMatchesInterruptionFilter;
private int mVisibilityOverride;
+ private int mSuppressedVisualEffects;
public Ranking() {}
@@ -861,6 +867,14 @@
return mVisibilityOverride;
}
+ /**
+ * Returns the type(s) of visual effects that should be suppressed for this notification.
+ * See {@link #SUPPRESSED_EFFECT_LIGHTS}, {@link #SUPPRESSED_EFFECT_PEEK}}.
+ */
+ public int getSuppressedVisualEffects() {
+ return mSuppressedVisualEffects;
+ }
+
/**
* Returns whether the notification matches the user's interruption
@@ -874,12 +888,14 @@
}
private void populate(String key, int rank, boolean isAmbient,
- boolean matchesInterruptionFilter, int visibilityOverride) {
+ boolean matchesInterruptionFilter, int visibilityOverride,
+ int suppressedVisualEffects) {
mKey = key;
mRank = rank;
mIsAmbient = isAmbient;
mMatchesInterruptionFilter = matchesInterruptionFilter;
mVisibilityOverride = visibilityOverride;
+ mSuppressedVisualEffects = suppressedVisualEffects;
}
}
@@ -896,6 +912,7 @@
private ArrayMap<String,Integer> mRanks;
private ArraySet<Object> mIntercepted;
private ArrayMap<String, Integer> mVisibilityOverrides;
+ private ArrayMap<String, Integer> mSuppressedVisualEffects;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -921,7 +938,7 @@
public boolean getRanking(String key, Ranking outRanking) {
int rank = getRank(key);
outRanking.populate(key, rank, isAmbient(key), !isIntercepted(key),
- getVisibilityOverride(key));
+ getVisibilityOverride(key), getSuppressedVisualEffects(key));
return rank >= 0;
}
@@ -959,11 +976,24 @@
buildVisibilityOverridesLocked();
}
}
- Integer overide = mVisibilityOverrides.get(key);
- if (overide == null) {
+ Integer override = mVisibilityOverrides.get(key);
+ if (override == null) {
return Ranking.VISIBILITY_NO_OVERRIDE;
}
- return overide.intValue();
+ return override.intValue();
+ }
+
+ private int getSuppressedVisualEffects(String key) {
+ synchronized (this) {
+ if (mSuppressedVisualEffects == null) {
+ buildSuppressedVisualEffectsLocked();
+ }
+ }
+ Integer suppressed = mSuppressedVisualEffects.get(key);
+ if (suppressed == null) {
+ return 0;
+ }
+ return suppressed.intValue();
}
// Locked by 'this'
@@ -992,6 +1022,15 @@
}
}
+ // Locked by 'this'
+ private void buildSuppressedVisualEffectsLocked() {
+ Bundle suppressedBundle = mRankingUpdate.getSuppressedVisualEffects();
+ mSuppressedVisualEffects = new ArrayMap<>(suppressedBundle.size());
+ for (String key: suppressedBundle.keySet()) {
+ mSuppressedVisualEffects.put(key, suppressedBundle.getInt(key));
+ }
+ }
+
// ----------- Parcelable
@Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 6fba900..1282fb1 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -28,13 +28,15 @@
private final String[] mInterceptedKeys;
private final int mFirstAmbientIndex;
private final Bundle mVisibilityOverrides;
+ private final Bundle mSuppressedVisualEffects;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
- Bundle visibilityOverrides, int firstAmbientIndex) {
+ Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects) {
mKeys = keys;
mFirstAmbientIndex = firstAmbientIndex;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
+ mSuppressedVisualEffects = suppressedVisualEffects;
}
public NotificationRankingUpdate(Parcel in) {
@@ -42,6 +44,7 @@
mFirstAmbientIndex = in.readInt();
mInterceptedKeys = in.readStringArray();
mVisibilityOverrides = in.readBundle();
+ mSuppressedVisualEffects = in.readBundle();
}
@Override
@@ -55,6 +58,7 @@
out.writeInt(mFirstAmbientIndex);
out.writeStringArray(mInterceptedKeys);
out.writeBundle(mVisibilityOverrides);
+ out.writeBundle(mSuppressedVisualEffects);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -83,4 +87,8 @@
public Bundle getVisibilityOverrides() {
return mVisibilityOverrides;
}
+
+ public Bundle getSuppressedVisualEffects() {
+ return mSuppressedVisualEffects;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 82f1b28..b3399d0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -77,6 +77,8 @@
private static final boolean DEFAULT_ALLOW_REMINDERS = true;
private static final boolean DEFAULT_ALLOW_EVENTS = true;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
+ private static final boolean DEFAULT_ALLOW_PEEK = true;
+ private static final boolean DEFAULT_ALLOW_LIGHTS = true;
private static final int XML_VERSION = 2;
private static final String ZEN_TAG = "zen";
@@ -91,6 +93,8 @@
private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
private static final String ALLOW_ATT_REMINDERS = "reminders";
private static final String ALLOW_ATT_EVENTS = "events";
+ private static final String ALLOW_ATT_PEEK = "peek";
+ private static final String ALLOW_ATT_LIGHTS = "lights";
private static final String CONDITION_TAG = "condition";
private static final String CONDITION_ATT_COMPONENT = "component";
@@ -122,6 +126,8 @@
public int allowCallsFrom = DEFAULT_SOURCE;
public int allowMessagesFrom = DEFAULT_SOURCE;
public int user = UserHandle.USER_SYSTEM;
+ public boolean allowPeek = DEFAULT_ALLOW_PEEK;
+ public boolean allowLights = DEFAULT_ALLOW_LIGHTS;
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -148,6 +154,8 @@
automaticRules.put(ids[i], rules[i]);
}
}
+ allowPeek = source.readInt() == 1;
+ allowLights = source.readInt() == 1;
}
@Override
@@ -175,22 +183,26 @@
} else {
dest.writeInt(0);
}
+ dest.writeInt(allowPeek ? 1 : 0);
+ dest.writeInt(allowLights ? 1 : 0);
}
@Override
public String toString() {
return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
- .append("user=").append(user)
- .append(",allowCalls=").append(allowCalls)
- .append(",allowRepeatCallers=").append(allowRepeatCallers)
- .append(",allowMessages=").append(allowMessages)
- .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
- .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
- .append(",allowReminders=").append(allowReminders)
- .append(",allowEvents=").append(allowEvents)
- .append(",automaticRules=").append(automaticRules)
- .append(",manualRule=").append(manualRule)
- .append(']').toString();
+ .append("user=").append(user)
+ .append(",allowCalls=").append(allowCalls)
+ .append(",allowRepeatCallers=").append(allowRepeatCallers)
+ .append(",allowMessages=").append(allowMessages)
+ .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
+ .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+ .append(",allowReminders=").append(allowReminders)
+ .append(",allowEvents=").append(allowEvents)
+ .append(",allowPeek=").append(allowPeek)
+ .append(",allowLights=").append(allowLights)
+ .append(",automaticRules=").append(automaticRules)
+ .append(",manualRule=").append(manualRule)
+ .append(']').toString();
}
private Diff diff(ZenModeConfig to) {
@@ -222,6 +234,12 @@
if (allowEvents != to.allowEvents) {
d.addLine("allowEvents", allowEvents, to.allowEvents);
}
+ if (allowPeek != to.allowPeek) {
+ d.addLine("allowPeek", allowPeek, to.allowPeek);
+ }
+ if (allowLights != to.allowLights) {
+ d.addLine("allowLights", allowLights, to.allowLights);
+ }
final ArraySet<String> allRules = new ArraySet<>();
addKeys(allRules, automaticRules);
addKeys(allRules, to.automaticRules);
@@ -319,6 +337,8 @@
&& other.allowMessagesFrom == allowMessagesFrom
&& other.allowReminders == allowReminders
&& other.allowEvents == allowEvents
+ && other.allowPeek == allowPeek
+ && other.allowLights == allowLights
&& other.user == user
&& Objects.equals(other.automaticRules, automaticRules)
&& Objects.equals(other.manualRule, manualRule);
@@ -327,7 +347,8 @@
@Override
public int hashCode() {
return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
- allowMessagesFrom, allowReminders, allowEvents, user, automaticRules, manualRule);
+ allowMessagesFrom, allowReminders, allowEvents, allowPeek, allowLights,
+ user, automaticRules, manualRule);
}
private static String toDayList(int[] days) {
@@ -412,6 +433,8 @@
rt.allowCallsFrom = DEFAULT_SOURCE;
rt.allowMessagesFrom = DEFAULT_SOURCE;
}
+ rt.allowPeek = safeBoolean(parser, ALLOW_ATT_PEEK, DEFAULT_ALLOW_PEEK);
+ rt.allowLights = safeBoolean(parser, ALLOW_ATT_LIGHTS, DEFAULT_ALLOW_LIGHTS);
} else if (MANUAL_TAG.equals(tag)) {
rt.manualRule = readRuleXml(parser);
} else if (AUTOMATIC_TAG.equals(tag)) {
@@ -440,6 +463,8 @@
out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
+ out.attribute(null, ALLOW_ATT_PEEK, Boolean.toString(allowPeek));
+ out.attribute(null, ALLOW_ATT_LIGHTS, Boolean.toString(allowLights));
out.endTag(null, ALLOW_TAG);
if (manualRule != null) {
@@ -611,9 +636,17 @@
if (allowRepeatCallers) {
priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
}
+ int suppressedVisualEffects = 0;
+ if (!allowPeek) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+ }
+ if (!allowLights) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+ }
priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
- return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders);
+ return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
+ suppressedVisualEffects);
}
private static int sourceToPrioritySenders(int source, int def) {
@@ -645,6 +678,10 @@
allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
allowMessagesFrom);
+ if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+ allowPeek = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_PEEK) == 0;
+ allowLights = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_LIGHTS) == 0;
+ }
}
public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
new file mode 100644
index 0000000..087eb61
--- /dev/null
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+package android.service.quicksettings;
+
+import android.content.ComponentName;
+import android.service.quicksettings.Tile;
+
+/**
+ * @hide
+ */
+interface IQSService {
+ void updateQsTile(in Tile tile);
+}
diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl
new file mode 100644
index 0000000..6b46bee5
--- /dev/null
+++ b/core/java/android/service/quicksettings/IQSTileService.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package android.service.quicksettings;
+
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.IQSService;
+
+/**
+ * @hide
+ */
+oneway interface IQSTileService {
+ void setQSTile(in Tile tile);
+ void onTileAdded();
+ void onTileRemoved();
+ void onStartListening();
+ void onStopListening();
+ void onClick();
+}
diff --git a/core/java/android/service/quicksettings/Tile.aidl b/core/java/android/service/quicksettings/Tile.aidl
new file mode 100644
index 0000000..0373326
--- /dev/null
+++ b/core/java/android/service/quicksettings/Tile.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.quicksettings;
+
+parcelable Tile;
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
new file mode 100644
index 0000000..c8ae171
--- /dev/null
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -0,0 +1,186 @@
+/*
+ * 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.service.quicksettings;
+
+import android.content.ComponentName;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * A Tile holds the state of a tile that will be displayed
+ * in Quick Settings.
+ *
+ * A tile in Quick Settings exists as an icon with an accompanied label.
+ * It also may have content description for accessibility usability.
+ * The style and layout of the tile may change to match a given
+ * device.
+ */
+public final class Tile implements Parcelable {
+
+ private static final String TAG = "Tile";
+
+ private ComponentName mComponentName;
+ private IQSService mService;
+ private Icon mIcon;
+ private CharSequence mLabel;
+ private CharSequence mContentDescription;
+
+ /**
+ * @hide
+ */
+ public Tile(Parcel source) {
+ readFromParcel(source);
+ }
+
+ /**
+ * @hide
+ */
+ public Tile(ComponentName componentName, IQSService service) {
+ mComponentName = componentName;
+ mService = service;
+ }
+
+ /**
+ * @hide
+ */
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * Gets the current icon for the tile.
+ */
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Sets the current icon for the tile.
+ *
+ * This icon is expected to be white on alpha, and may be
+ * tinted by the system to match it's theme.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param icon New icon to show.
+ */
+ public void setIcon(Icon icon) {
+ this.mIcon = icon;
+ }
+
+ /**
+ * Gets the current label for the tile.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Sets the current label for the tile.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param label New label to show.
+ */
+ public void setLabel(CharSequence label) {
+ this.mLabel = label;
+ }
+
+ /**
+ * Gets the current content description for the tile.
+ */
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Sets the current content description for the tile.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param contentDescription New content description to use.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ this.mContentDescription = contentDescription;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Pushes the state of the Tile to Quick Settings to be displayed.
+ */
+ public void updateTile() {
+ try {
+ mService.updateQsTile(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't update tile");
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongInterface(mService);
+ if (mComponentName != null) {
+ dest.writeByte((byte) 1);
+ mComponentName.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ if (mIcon != null) {
+ dest.writeByte((byte) 1);
+ mIcon.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ TextUtils.writeToParcel(mLabel, dest, flags);
+ TextUtils.writeToParcel(mContentDescription, dest, flags);
+ }
+
+ private void readFromParcel(Parcel source) {
+ mService = IQSService.Stub.asInterface(source.readStrongBinder());
+ if (source.readByte() != 0) {
+ mComponentName = ComponentName.CREATOR.createFromParcel(source);
+ } else {
+ mComponentName = null;
+ }
+ if (source.readByte() != 0) {
+ mIcon = Icon.CREATOR.createFromParcel(source);
+ } else {
+ mIcon = null;
+ }
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ }
+
+ public static final Creator<Tile> CREATOR = new Creator<Tile>() {
+ @Override
+ public Tile createFromParcel(Parcel source) {
+ return new Tile(source);
+ }
+
+ @Override
+ public Tile[] newArray(int size) {
+ return new Tile[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
new file mode 100644
index 0000000..eba4c6f
--- /dev/null
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -0,0 +1,206 @@
+/*
+ * 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.service.quicksettings;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+
+/**
+ * A QSTileService provides the user a tile that can be added to Quick Settings.
+ * Quick Settings is a space provided that allows the user to change settings and
+ * take quick actions without leaving the context of their current app.
+ *
+ * <p>The lifecycle of a QSTileService is different from some other services in
+ * that it may be unbound during parts of its lifecycle. Any of the following
+ * lifecycle events can happen indepently in a separate binding/creation of the
+ * service.</p>
+ *
+ * <ul>
+ * <li>When a tile is added by the user its QSTileService will be bound to and
+ * {@link #onTileAdded()} will be called.</li>
+ *
+ * <li>When a tile should be up to date and listing will be indicated by
+ * {@link #onStartListening()} and {@link #onStopListening()}.</li>
+ *
+ * <li>When the user removes a tile from Quick Settings {@link #onStopListening()}
+ * will be called.</li>
+ * </ul>
+ * <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
+ * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
+ * The label and icon for the service will be used as the default label and
+ * icon for the tile. Here is an example QSTileService declaration.</p>
+ * <pre class="prettyprint">
+ * {@literal
+ * <service
+ * android:name=".MyQSTileService"
+ * android:label="@string/my_default_tile_label"
+ * android:icon="@drawable/my_default_icon_label"
+ * android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
+ * <intent-filter>
+ * <action android:name="android.intent.action.QS_TILE" />
+ * </intent-filter>
+ * </service>}
+ * </pre>
+ *
+ * @see Tile Tile for details about the UI of a Quick Settings Tile.
+ */
+public class TileService extends Service {
+
+ /**
+ * Action that identifies a Service as being a QSTileService.
+ */
+ public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+
+ private final H mHandler = new H(Looper.getMainLooper());
+
+ private boolean mListening = false;
+ private Tile mTile;
+
+ /**
+ * Called when the user adds this tile to Quick Settings.
+ * <p/>
+ * Note that this is not guaranteed to be called between {@link #onCreate()}
+ * and {@link #onStartListening()}, it will only be called when the tile is added
+ * and not on subsequent binds.
+ */
+ public void onTileAdded() {
+ }
+
+ /**
+ * Called when the user removes this tile from Quick Settings.
+ */
+ public void onTileRemoved() {
+ }
+
+ /**
+ * Called when this tile moves into a listening state.
+ * <p/>
+ * When this tile is in a listening state it is expected to keep the
+ * UI up to date. Any listeners or callbacks needed to keep this tile
+ * up to date should be registered here and unregistered in {@link #onStopListening()}.
+ *
+ * @see #getQsTile()
+ * @see Tile#updateTile()
+ */
+ public void onStartListening() {
+ }
+
+ /**
+ * Called when this tile moves out of the listening state.
+ */
+ public void onStopListening() {
+ }
+
+ /**
+ * Called when the user clicks on this tile.
+ */
+ public void onClick() {
+ }
+
+ /**
+ * Gets the {@link Tile} for this service.
+ * <p/>
+ * This tile may be used to get or set the current state for this
+ * tile. This tile is only valid for updates between {@link #onStartListening()}
+ * and {@link #onStopListening()}.
+ */
+ public final Tile getQsTile() {
+ return mTile;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new IQSTileService.Stub() {
+ @Override
+ public void setQSTile(Tile tile) throws RemoteException {
+ mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget();
+ }
+
+ @Override
+ public void onTileRemoved() throws RemoteException {
+ mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED);
+ }
+
+ @Override
+ public void onTileAdded() throws RemoteException {
+ mHandler.sendEmptyMessage(H.MSG_TILE_ADDED);
+ }
+
+ @Override
+ public void onStopListening() throws RemoteException {
+ mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING);
+ }
+
+ @Override
+ public void onStartListening() throws RemoteException {
+ mHandler.sendEmptyMessage(H.MSG_START_LISTENING);
+ }
+
+ @Override
+ public void onClick() throws RemoteException {
+ mHandler.sendEmptyMessage(H.MSG_TILE_CLICKED);
+ }
+ };
+ }
+
+ private class H extends Handler {
+ private static final int MSG_SET_TILE = 1;
+ private static final int MSG_START_LISTENING = 2;
+ private static final int MSG_STOP_LISTENING = 3;
+ private static final int MSG_TILE_ADDED = 4;
+ private static final int MSG_TILE_REMOVED = 5;
+ private static final int MSG_TILE_CLICKED = 6;
+
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_TILE:
+ mTile = (Tile) msg.obj;
+ break;
+ case MSG_TILE_ADDED:
+ TileService.this.onTileRemoved();
+ break;
+ case MSG_TILE_REMOVED:
+ TileService.this.onTileAdded();
+ break;
+ case MSG_START_LISTENING:
+ if (mListening) {
+ mListening = false;
+ TileService.this.onStopListening();
+ }
+ break;
+ case MSG_STOP_LISTENING:
+ if (!mListening) {
+ mListening = true;
+ TileService.this.onStartListening();
+ }
+ break;
+ case MSG_TILE_CLICKED:
+ TileService.this.onClick();
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index 463eb5b..ebe3f47 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -52,9 +52,21 @@
}
public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
- throws PackageManager.NameNotFoundException, RemoteException {
- this(pm, AppGlobals.getPackageManager().getServiceInfo(comp,
- PackageManager.GET_META_DATA, userHandle));
+ throws PackageManager.NameNotFoundException {
+ this(pm, getServiceInfoOrThrow(comp, userHandle));
+ }
+
+ static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
+ throws PackageManager.NameNotFoundException {
+ try {
+ ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(comp,
+ PackageManager.GET_META_DATA, userHandle);
+ if (si != null) {
+ return si;
+ }
+ } catch (RemoteException e) {
+ }
+ throw new PackageManager.NameNotFoundException(comp.toString());
}
public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) {
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index a88eead..2b882d3 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -107,6 +107,14 @@
Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
+ "," + channelCount + ")");
}
+ if (audioFormat != AudioFormat.ENCODING_PCM_8BIT ||
+ audioFormat != AudioFormat.ENCODING_PCM_16BIT ||
+ audioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
+ Log.e(TAG, "Audio format encoding " + audioFormat + " not supported. Please use one " +
+ "of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
+ "AudioFormat.ENCODING_PCM_FLOAT");
+ }
+
FileChannel fileChannel = null;
synchronized (mStateLock) {
if (mStatusCode == TextToSpeech.STOPPED) {
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index f850f10..a6fb543 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.media.AudioFormat;
import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
import android.util.Log;
@@ -122,6 +123,13 @@
public int start(int sampleRateInHz, int audioFormat, int channelCount) {
if (DBG) Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat + "," + channelCount
+ ")");
+ if (audioFormat != AudioFormat.ENCODING_PCM_8BIT ||
+ audioFormat != AudioFormat.ENCODING_PCM_16BIT ||
+ audioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
+ Log.w(TAG, "Audio format encoding " + audioFormat + " not supported. Please use one " +
+ "of AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT or " +
+ "AudioFormat.ENCODING_PCM_FLOAT");
+ }
int channelConfig = BlockingAudioTrack.getChannelConfig(channelCount);
diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java
index e32438b..6c7a217 100644
--- a/core/java/android/speech/tts/SynthesisCallback.java
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -15,6 +15,14 @@
*/
package android.speech.tts;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.media.AudioFormat;
+import android.speech.tts.TextToSpeech;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A callback to return speech data synthesized by a text to speech engine.
*
@@ -31,6 +39,13 @@
* All methods can be only called on the synthesis thread.
*/
public interface SynthesisCallback {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.ENCODING_PCM_FLOAT})
+ public @interface SupportedAudioFormat {};
+
/**
* @return the maximum number of bytes that the TTS engine can pass in a single call of
* {@link #audioAvailable}. Calls to {@link #audioAvailable} with data lengths
@@ -38,6 +53,7 @@
*/
public int getMaxBufferSize();
+ // TODO: Replace reference to Android N to an API level when the API level for N is decided.
/**
* The service should call this when it starts to synthesize audio for this
* request.
@@ -47,12 +63,16 @@
*
* @param sampleRateInHz Sample rate in HZ of the generated audio.
* @param audioFormat Audio format of the generated audio. Must be one of
- * the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+ * {@link AudioFormat#ENCODING_PCM_8BIT} or
+ * {@link AudioFormat#ENCODING_PCM_16BIT}. Can also be
+ * {@link AudioFormat#ENCODING_PCM_FLOAT} when targetting Android N and
+ * above.
* @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
* @return {@link TextToSpeech#SUCCESS}, {@link TextToSpeech#ERROR} or
* {@link TextToSpeech#STOPPED}.
*/
- public int start(int sampleRateInHz, int audioFormat, int channelCount);
+ public int start(int sampleRateInHz, @SupportedAudioFormat int audioFormat,
+ @IntRange(from=1,to=2) int channelCount);
/**
* The service should call this method when synthesized audio is ready for consumption.
@@ -102,7 +122,7 @@
* @param errorCode Error code to pass to the client. One of the ERROR_ values from
* {@link TextToSpeech}
*/
- public void error(int errorCode);
+ public void error(@TextToSpeech.Error int errorCode);
/**
* Check if {@link #start} was called or not.
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index eae4329..61c33ff 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts;
+import android.annotation.IntDef;
import android.annotation.RawRes;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -38,6 +39,8 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -75,6 +78,12 @@
*/
public static final int STOPPED = -2;
+ /** @hide */
+ @IntDef({ERROR_SYNTHESIS, ERROR_SERVICE, ERROR_OUTPUT, ERROR_NETWORK, ERROR_NETWORK_TIMEOUT,
+ ERROR_INVALID_REQUEST, ERROR_NOT_INSTALLED_YET})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Error {}
+
/**
* Denotes a failure of a TTS engine to synthesize the given input.
*/
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 0d5c135..c1d23bb 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -49,6 +49,7 @@
return location < mList.length ? mList[location] : null;
}
+ @Nullable
public Locale getPrimary() {
return mList.length == 0 ? null : get(0);
}
@@ -179,6 +180,12 @@
}
}
+ @Nullable
+ public Locale getBestMatch(String[] locales) {
+ // TODO: Fix this to actually do locale negotiation and choose the best match
+ return getPrimary();
+ }
+
private final static Object sLock = new Object();
@GuardedBy("sLock")
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index cebb8f0..f17a16c 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -15,10 +15,6 @@
package android.util;
import android.graphics.Path;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
/**
* @hide
@@ -27,678 +23,112 @@
static final String LOGTAG = PathParser.class.getSimpleName();
/**
- * @param pathData The string representing a path, the same as "d" string in svg file.
+ * @param pathString The string representing a path, the same as "d" string in svg file.
* @return the generated Path object.
*/
- public static Path createPathFromPathData(String pathData) {
+ public static Path createPathFromPathData(String pathString) {
+ if (pathString == null) {
+ throw new IllegalArgumentException("Path string can not be null.");
+ }
Path path = new Path();
- PathDataNode[] nodes = createNodesFromPathData(pathData);
- if (nodes != null) {
- try {
- PathDataNode.nodesToPath(nodes, path);
- } catch (RuntimeException e) {
- throw new RuntimeException("Error in parsing " + pathData, e);
- }
- return path;
+ boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
+ pathString.length());
+ if (!hasValidPathData) {
+ throw new IllegalArgumentException("Path string: " + pathString +
+ " does not contain valid path data");
}
- return null;
+ return path;
}
/**
- * @param pathData The string representing a path, the same as "d" string in svg file.
- * @return an array of the PathDataNode.
+ * Interpret PathData as path commands and insert the commands to the given path.
+ *
+ * @param data The source PathData to be converted.
+ * @param outPath The Path object where path commands will be inserted.
*/
- public static PathDataNode[] createNodesFromPathData(String pathData) {
- if (pathData == null) {
- return null;
- }
- int start = 0;
- int end = 1;
-
- ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
- while (end < pathData.length()) {
- end = nextStart(pathData, end);
- String s = pathData.substring(start, end).trim();
- if (s.length() > 0) {
- float[] val = getFloats(s);
- addNode(list, s.charAt(0), val);
- }
-
- start = end;
- end++;
- }
- if ((end - start) == 1 && start < pathData.length()) {
- addNode(list, pathData.charAt(start), new float[0]);
- }
- return list.toArray(new PathDataNode[list.size()]);
+ public static void createPathFromPathData(Path outPath, PathData data) {
+ nCreatePathFromPathData(outPath.mNativePath, data.mNativePathData);
}
/**
- * @param source The array of PathDataNode to be duplicated.
- * @return a deep copy of the <code>source</code>.
- */
- public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
- if (source == null) {
- return null;
- }
- PathDataNode[] copy = new PathParser.PathDataNode[source.length];
- for (int i = 0; i < source.length; i ++) {
- copy[i] = new PathDataNode(source[i]);
- }
- return copy;
- }
-
- /**
- * @param nodesFrom The source path represented in an array of PathDataNode
- * @param nodesTo The target path represented in an array of PathDataNode
+ * @param pathDataFrom The source path represented in PathData
+ * @param pathDataTo The target path represented in PathData
* @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
*/
- public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
- if (nodesFrom == null || nodesTo == null) {
- return false;
- }
-
- if (nodesFrom.length != nodesTo.length) {
- return false;
- }
-
- for (int i = 0; i < nodesFrom.length; i ++) {
- if (nodesFrom[i].mType != nodesTo[i].mType
- || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
- return false;
- }
- }
- return true;
+ public static boolean canMorph(PathData pathDataFrom, PathData pathDataTo) {
+ return nCanMorph(pathDataFrom.mNativePathData, pathDataTo.mNativePathData);
}
/**
- * Update the target's data to match the source.
- * Before calling this, make sure canMorph(target, source) is true.
+ * PathData class is a wrapper around the native PathData object, which contains
+ * the result of parsing a path string. Specifically, there are verbs and points
+ * associated with each verb stored in PathData. This data can then be used to
+ * generate commands to manipulate a Path.
+ */
+ public static class PathData {
+ long mNativePathData = 0;
+ public PathData() {
+ mNativePathData = nCreateEmptyPathData();
+ }
+
+ public PathData(PathData data) {
+ mNativePathData = nCreatePathData(data.mNativePathData);
+ }
+
+ public PathData(String pathString) {
+ mNativePathData = nCreatePathDataFromString(pathString, pathString.length());
+ if (mNativePathData == 0) {
+ throw new IllegalArgumentException("Invalid pathData: " + pathString);
+ }
+ }
+
+ /**
+ * Update the path data to match the source.
+ * Before calling this, make sure canMorph(target, source) is true.
+ *
+ * @param source The source path represented in PathData
+ */
+ public void setPathData(PathData source) {
+ nSetPathData(mNativePathData, source.mNativePathData);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mNativePathData != 0) {
+ nFinalize(mNativePathData);
+ mNativePathData = 0;
+ }
+ super.finalize();
+ }
+ }
+
+ /**
+ * Interpolate between the <code>fromData</code> and <code>toData</code> according to the
+ * <code>fraction</code>, and put the resulting path data into <code>outData</code>.
*
- * @param target The target path represented in an array of PathDataNode
- * @param source The source path represented in an array of PathDataNode
+ * @param outData The resulting PathData of the interpolation
+ * @param fromData The start value as a PathData.
+ * @param toData The end value as a PathData
+ * @param fraction The fraction to interpolate.
*/
- public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
- for (int i = 0; i < source.length; i ++) {
- target[i].mType = source[i].mType;
- for (int j = 0; j < source[i].mParams.length; j ++) {
- target[i].mParams[j] = source[i].mParams[j];
- }
- }
+ public static boolean interpolatePathData(PathData outData, PathData fromData, PathData toData,
+ float fraction) {
+ return nInterpolatePathData(outData.mNativePathData, fromData.mNativePathData,
+ toData.mNativePathData, fraction);
}
- private static int nextStart(String s, int end) {
- char c;
-
- while (end < s.length()) {
- c = s.charAt(end);
- // Note that 'e' or 'E' are not valid path commands, but could be
- // used for floating point numbers' scientific notation.
- // Therefore, when searching for next command, we should ignore 'e'
- // and 'E'.
- if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
- && c != 'e' && c != 'E') {
- return end;
- }
- end++;
- }
- return end;
- }
-
- private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
- list.add(new PathDataNode(cmd, val));
- }
-
- private static class ExtractFloatResult {
- // We need to return the position of the next separator and whether the
- // next float starts with a '-' or a '.'.
- int mEndPosition;
- boolean mEndWithNegOrDot;
- }
-
- /**
- * Parse the floats in the string.
- * This is an optimized version of parseFloat(s.split(",|\\s"));
- *
- * @param s the string containing a command and list of floats
- * @return array of floats
- */
- private static float[] getFloats(String s) {
- if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
- return new float[0];
- }
- try {
- float[] results = new float[s.length()];
- int count = 0;
- int startPosition = 1;
- int endPosition = 0;
-
- ExtractFloatResult result = new ExtractFloatResult();
- int totalLength = s.length();
-
- // The startPosition should always be the first character of the
- // current number, and endPosition is the character after the current
- // number.
- while (startPosition < totalLength) {
- extract(s, startPosition, result);
- endPosition = result.mEndPosition;
-
- if (startPosition < endPosition) {
- results[count++] = Float.parseFloat(
- s.substring(startPosition, endPosition));
- }
-
- if (result.mEndWithNegOrDot) {
- // Keep the '-' or '.' sign with next number.
- startPosition = endPosition;
- } else {
- startPosition = endPosition + 1;
- }
- }
- return Arrays.copyOf(results, count);
- } catch (NumberFormatException e) {
- throw new RuntimeException("error in parsing \"" + s + "\"", e);
- }
- }
-
- /**
- * Calculate the position of the next comma or space or negative sign
- * @param s the string to search
- * @param start the position to start searching
- * @param result the result of the extraction, including the position of the
- * the starting position of next number, whether it is ending with a '-'.
- */
- private static void extract(String s, int start, ExtractFloatResult result) {
- // Now looking for ' ', ',', '.' or '-' from the start.
- int currentIndex = start;
- boolean foundSeparator = false;
- result.mEndWithNegOrDot = false;
- boolean secondDot = false;
- boolean isExponential = false;
- for (; currentIndex < s.length(); currentIndex++) {
- boolean isPrevExponential = isExponential;
- isExponential = false;
- char currentChar = s.charAt(currentIndex);
- switch (currentChar) {
- case ' ':
- case ',':
- foundSeparator = true;
- break;
- case '-':
- // The negative sign following a 'e' or 'E' is not a separator.
- if (currentIndex != start && !isPrevExponential) {
- foundSeparator = true;
- result.mEndWithNegOrDot = true;
- }
- break;
- case '.':
- if (!secondDot) {
- secondDot = true;
- } else {
- // This is the second dot, and it is considered as a separator.
- foundSeparator = true;
- result.mEndWithNegOrDot = true;
- }
- break;
- case 'e':
- case 'E':
- isExponential = true;
- break;
- }
- if (foundSeparator) {
- break;
- }
- }
- // When there is nothing found, then we put the end position to the end
- // of the string.
- result.mEndPosition = currentIndex;
- }
-
- /**
- * Each PathDataNode represents one command in the "d" attribute of the svg
- * file.
- * An array of PathDataNode can represent the whole "d" attribute.
- */
- public static class PathDataNode {
- private char mType;
- private float[] mParams;
-
- private PathDataNode(char type, float[] params) {
- mType = type;
- mParams = params;
- }
-
- private PathDataNode(PathDataNode n) {
- mType = n.mType;
- mParams = Arrays.copyOf(n.mParams, n.mParams.length);
- }
-
- /**
- * Convert an array of PathDataNode to Path.
- *
- * @param node The source array of PathDataNode.
- * @param path The target Path object.
- */
- public static void nodesToPath(PathDataNode[] node, Path path) {
- float[] current = new float[6];
- char previousCommand = 'm';
- for (int i = 0; i < node.length; i++) {
- addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
- previousCommand = node[i].mType;
- }
- }
-
- /**
- * The current PathDataNode will be interpolated between the
- * <code>nodeFrom</code> and <code>nodeTo</code> according to the
- * <code>fraction</code>.
- *
- * @param nodeFrom The start value as a PathDataNode.
- * @param nodeTo The end value as a PathDataNode
- * @param fraction The fraction to interpolate.
- */
- public void interpolatePathDataNode(PathDataNode nodeFrom,
- PathDataNode nodeTo, float fraction) {
- for (int i = 0; i < nodeFrom.mParams.length; i++) {
- mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
- + nodeTo.mParams[i] * fraction;
- }
- }
-
- private static void addCommand(Path path, float[] current,
- char previousCmd, char cmd, float[] val) {
-
- int incr = 2;
- float currentX = current[0];
- float currentY = current[1];
- float ctrlPointX = current[2];
- float ctrlPointY = current[3];
- float currentSegmentStartX = current[4];
- float currentSegmentStartY = current[5];
- float reflectiveCtrlPointX;
- float reflectiveCtrlPointY;
-
- switch (cmd) {
- case 'z':
- case 'Z':
- path.close();
- // Path is closed here, but we need to move the pen to the
- // closed position. So we cache the segment's starting position,
- // and restore it here.
- currentX = currentSegmentStartX;
- currentY = currentSegmentStartY;
- ctrlPointX = currentSegmentStartX;
- ctrlPointY = currentSegmentStartY;
- path.moveTo(currentX, currentY);
- break;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- incr = 2;
- break;
- case 'h':
- case 'H':
- case 'v':
- case 'V':
- incr = 1;
- break;
- case 'c':
- case 'C':
- incr = 6;
- break;
- case 's':
- case 'S':
- case 'q':
- case 'Q':
- incr = 4;
- break;
- case 'a':
- case 'A':
- incr = 7;
- break;
- }
-
- for (int k = 0; k < val.length; k += incr) {
- switch (cmd) {
- case 'm': // moveto - Start a new sub-path (relative)
- currentX += val[k + 0];
- currentY += val[k + 1];
- if (k > 0) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- path.rLineTo(val[k + 0], val[k + 1]);
- } else {
- path.rMoveTo(val[k + 0], val[k + 1]);
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'M': // moveto - Start a new sub-path
- currentX = val[k + 0];
- currentY = val[k + 1];
- if (k > 0) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- path.lineTo(val[k + 0], val[k + 1]);
- } else {
- path.moveTo(val[k + 0], val[k + 1]);
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'l': // lineto - Draw a line from the current point (relative)
- path.rLineTo(val[k + 0], val[k + 1]);
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'L': // lineto - Draw a line from the current point
- path.lineTo(val[k + 0], val[k + 1]);
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'h': // horizontal lineto - Draws a horizontal line (relative)
- path.rLineTo(val[k + 0], 0);
- currentX += val[k + 0];
- break;
- case 'H': // horizontal lineto - Draws a horizontal line
- path.lineTo(val[k + 0], currentY);
- currentX = val[k + 0];
- break;
- case 'v': // vertical lineto - Draws a vertical line from the current point (r)
- path.rLineTo(0, val[k + 0]);
- currentY += val[k + 0];
- break;
- case 'V': // vertical lineto - Draws a vertical line from the current point
- path.lineTo(currentX, val[k + 0]);
- currentY = val[k + 0];
- break;
- case 'c': // curveto - Draws a cubic Bézier curve (relative)
- path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
-
- ctrlPointX = currentX + val[k + 2];
- ctrlPointY = currentY + val[k + 3];
- currentX += val[k + 4];
- currentY += val[k + 5];
-
- break;
- case 'C': // curveto - Draws a cubic Bézier curve
- path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
- currentX = val[k + 4];
- currentY = val[k + 5];
- ctrlPointX = val[k + 2];
- ctrlPointY = val[k + 3];
- break;
- case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1],
- val[k + 2], val[k + 3]);
-
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 'q': // Draws a quadratic Bézier (relative)
- path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'Q': // Draws a quadratic Bézier
- path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = currentX + reflectiveCtrlPointX;
- ctrlPointY = currentY + reflectiveCtrlPointY;
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'T': // Draws a quadratic Bézier curve (reflective control point)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = reflectiveCtrlPointX;
- ctrlPointY = reflectiveCtrlPointY;
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'a': // Draws an elliptical arc
- // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
- drawArc(path,
- currentX,
- currentY,
- val[k + 5] + currentX,
- val[k + 6] + currentY,
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX += val[k + 5];
- currentY += val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- case 'A': // Draws an elliptical arc
- drawArc(path,
- currentX,
- currentY,
- val[k + 5],
- val[k + 6],
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX = val[k + 5];
- currentY = val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- }
- previousCmd = cmd;
- }
- current[0] = currentX;
- current[1] = currentY;
- current[2] = ctrlPointX;
- current[3] = ctrlPointY;
- current[4] = currentSegmentStartX;
- current[5] = currentSegmentStartY;
- }
-
- private static void drawArc(Path p,
- float x0,
- float y0,
- float x1,
- float y1,
- float a,
- float b,
- float theta,
- boolean isMoreThanHalf,
- boolean isPositiveArc) {
-
- /* Convert rotation angle from degrees to radians */
- double thetaD = Math.toRadians(theta);
- /* Pre-compute rotation matrix entries */
- double cosTheta = Math.cos(thetaD);
- double sinTheta = Math.sin(thetaD);
- /* Transform (x0, y0) and (x1, y1) into unit space */
- /* using (inverse) rotation, followed by (inverse) scale */
- double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
- double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
- double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
- double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
- /* Compute differences and averages */
- double dx = x0p - x1p;
- double dy = y0p - y1p;
- double xm = (x0p + x1p) / 2;
- double ym = (y0p + y1p) / 2;
- /* Solve for intersecting unit circles */
- double dsq = dx * dx + dy * dy;
- if (dsq == 0.0) {
- Log.w(LOGTAG, " Points are coincident");
- return; /* Points are coincident */
- }
- double disc = 1.0 / dsq - 1.0 / 4.0;
- if (disc < 0.0) {
- Log.w(LOGTAG, "Points are too far apart " + dsq);
- float adjust = (float) (Math.sqrt(dsq) / 1.99999);
- drawArc(p, x0, y0, x1, y1, a * adjust,
- b * adjust, theta, isMoreThanHalf, isPositiveArc);
- return; /* Points are too far apart */
- }
- double s = Math.sqrt(disc);
- double sdx = s * dx;
- double sdy = s * dy;
- double cx;
- double cy;
- if (isMoreThanHalf == isPositiveArc) {
- cx = xm - sdy;
- cy = ym + sdx;
- } else {
- cx = xm + sdy;
- cy = ym - sdx;
- }
-
- double eta0 = Math.atan2((y0p - cy), (x0p - cx));
-
- double eta1 = Math.atan2((y1p - cy), (x1p - cx));
-
- double sweep = (eta1 - eta0);
- if (isPositiveArc != (sweep >= 0)) {
- if (sweep > 0) {
- sweep -= 2 * Math.PI;
- } else {
- sweep += 2 * Math.PI;
- }
- }
-
- cx *= a;
- cy *= b;
- double tcx = cx;
- cx = cx * cosTheta - cy * sinTheta;
- cy = tcx * sinTheta + cy * cosTheta;
-
- arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
- }
-
- /**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
- private static void arcToBezier(Path p,
- double cx,
- double cy,
- double a,
- double b,
- double e1x,
- double e1y,
- double theta,
- double start,
- double sweep) {
- // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
- // and http://www.spaceroots.org/documents/ellipse/node22.html
-
- // Maximum of 45 degrees per cubic Bezier segment
- int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
-
- double eta1 = start;
- double cosTheta = Math.cos(theta);
- double sinTheta = Math.sin(theta);
- double cosEta1 = Math.cos(eta1);
- double sinEta1 = Math.sin(eta1);
- double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
- double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
- double anglePerSegment = sweep / numSegments;
- for (int i = 0; i < numSegments; i++) {
- double eta2 = eta1 + anglePerSegment;
- double sinEta2 = Math.sin(eta2);
- double cosEta2 = Math.cos(eta2);
- double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
- double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
- double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
- double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
- double tanDiff2 = Math.tan((eta2 - eta1) / 2);
- double alpha =
- Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
- double q1x = e1x + alpha * ep1x;
- double q1y = e1y + alpha * ep1y;
- double q2x = e2x - alpha * ep2x;
- double q2y = e2y - alpha * ep2y;
-
- p.cubicTo((float) q1x,
- (float) q1y,
- (float) q2x,
- (float) q2y,
- (float) e2x,
- (float) e2y);
- eta1 = eta2;
- e1x = e2x;
- e1y = e2y;
- ep1x = ep2x;
- ep1y = ep2y;
- }
- }
- }
+ // Native functions are defined below.
+ private static native boolean nParseStringForPath(long pathPtr, String pathString,
+ int stringLength);
+ private static native void nCreatePathFromPathData(long outPathPtr, long pathData);
+ private static native long nCreateEmptyPathData();
+ private static native long nCreatePathData(long nativePtr);
+ private static native long nCreatePathDataFromString(String pathString, int stringLength);
+ private static native boolean nInterpolatePathData(long outDataPtr, long fromDataPtr,
+ long toDataPtr, float fraction);
+ private static native void nFinalize(long nativePtr);
+ private static native boolean nCanMorph(long fromDataPtr, long toDataPtr);
+ private static native void nSetPathData(long outDataPtr, long fromDataPtr);
}
+
+
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index c2c247e..d563f51 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -17,6 +17,9 @@
package android.view;
import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import java.util.ArrayList;
import java.util.Collections;
@@ -173,6 +176,7 @@
// Note: This sort is stable.
mSequentialFocusComparator.setRoot(root);
mSequentialFocusComparator.setIsLayoutRtl(root.isLayoutRtl());
+ mSequentialFocusComparator.setFocusables(focusables);
Collections.sort(focusables, mSequentialFocusComparator);
} finally {
mSequentialFocusComparator.recycle();
@@ -598,8 +602,16 @@
+ "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
}
+ private static final boolean isValidId(final int id) {
+ return id != 0 && id != View.NO_ID;
+ }
+
/**
* Sorts views according to their visual layout and geometry for default tab order.
+ * If views are part of a focus chain (nextFocusForwardId), then they are all grouped
+ * together. The head of the chain is used to determine the order of the chain and is
+ * first in the order and the tail of the chain is the last in the order. The views
+ * in the middle of the chain can be arbitrary order.
* This is used for sequential focus traversal.
*/
private static final class SequentialFocusComparator implements Comparator<View> {
@@ -607,9 +619,15 @@
private final Rect mSecondRect = new Rect();
private ViewGroup mRoot;
private boolean mIsLayoutRtl;
+ private final SparseArray<View> mFocusables = new SparseArray<View>();
+ private final SparseBooleanArray mIsConnectedTo = new SparseBooleanArray();
+ private final ArrayMap<View, View> mHeadsOfChains = new ArrayMap<View, View>();
public void recycle() {
mRoot = null;
+ mFocusables.clear();
+ mHeadsOfChains.clear();
+ mIsConnectedTo.clear();
}
public void setRoot(ViewGroup root) {
@@ -620,11 +638,72 @@
mIsLayoutRtl = b;
}
+ public void setFocusables(ArrayList<View> focusables) {
+ for (int i = focusables.size() - 1; i >= 0; i--) {
+ final View view = focusables.get(i);
+ final int id = view.getId();
+ if (isValidId(id)) {
+ mFocusables.put(id, view);
+ }
+ final int nextId = view.getNextFocusForwardId();
+ if (isValidId(nextId)) {
+ mIsConnectedTo.put(nextId, true);
+ }
+ }
+
+ for (int i = focusables.size() - 1; i >= 0; i--) {
+ final View view = focusables.get(i);
+ final int nextId = view.getNextFocusForwardId();
+ if (isValidId(nextId) && !mIsConnectedTo.get(view.getId())) {
+ setHeadOfChain(view);
+ }
+ }
+ }
+
+ private void setHeadOfChain(View head) {
+ for (View view = head; view != null;
+ view = mFocusables.get(view.getNextFocusForwardId())) {
+ final View otherHead = mHeadsOfChains.get(view);
+ if (otherHead != null) {
+ if (otherHead == head) {
+ return; // This view has already had its head set properly
+ }
+ // A hydra -- multi-headed focus chain (e.g. A->C and B->C)
+ // Use the one we've already chosen instead and reset this chain.
+ view = head;
+ head = otherHead;
+ }
+ mHeadsOfChains.put(view, head);
+ }
+ }
+
public int compare(View first, View second) {
if (first == second) {
return 0;
}
+ // Order between views within a chain is immaterial -- next/previous is
+ // within a chain is handled elsewhere.
+ View firstHead = mHeadsOfChains.get(first);
+ View secondHead = mHeadsOfChains.get(second);
+ if (firstHead == secondHead && firstHead != null) {
+ if (first == firstHead) {
+ return -1; // first is the head, it should be first
+ } else if (second == firstHead) {
+ return 1; // second is the head, it should be first
+ } else if (isValidId(first.getNextFocusForwardId())) {
+ return -1; // first is not the end of the chain
+ } else {
+ return 1; // first is end of chain
+ }
+ }
+ if (firstHead != null) {
+ first = firstHead;
+ }
+ if (secondHead != null) {
+ second = secondHead;
+ }
+ // First see if they belong to the same focus chain.
getRect(first, mFirstRect);
getRect(second, mSecondRect);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7b5f5ab..64a046e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -289,8 +289,12 @@
/**
* Create a screenshot of the applications currently displayed.
+ *
+ * @param frameScale the scale to apply to the frame, only used when width = -1 and
+ * height = -1
*/
- Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight);
+ Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight,
+ float frameScale);
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 3fc70cc..b3cd8c11 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -186,6 +186,11 @@
void reportDropResult(IWindow window, boolean consumed);
/**
+ * Cancel the current drag operation.
+ */
+ void cancelDragAndDrop(IBinder dragToken);
+
+ /**
* Tell the OS that we've just dragged into a View that is willing to accept the drop
*/
void dragRecipientEntered(IWindow window);
diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java
index 0ee6714..49242bb 100644
--- a/core/java/android/view/MagnificationSpec.java
+++ b/core/java/android/view/MagnificationSpec.java
@@ -28,10 +28,21 @@
public class MagnificationSpec implements Parcelable {
private static final int MAX_POOL_SIZE = 20;
private static final SynchronizedPool<MagnificationSpec> sPool =
- new SynchronizedPool<MagnificationSpec>(MAX_POOL_SIZE);
+ new SynchronizedPool<>(MAX_POOL_SIZE);
+ /** The magnification scaling factor. */
public float scale = 1.0f;
+
+ /**
+ * The X coordinate, in unscaled screen-relative pixels, around which
+ * magnification is focused.
+ */
public float offsetX;
+
+ /**
+ * The Y coordinate, in unscaled screen-relative pixels, around which
+ * magnification is focused.
+ */
public float offsetY;
private MagnificationSpec() {
@@ -75,6 +86,12 @@
offsetY = 0.0f;
}
+ public void setTo(MagnificationSpec other) {
+ scale = other.scale;
+ offsetX = other.offsetX;
+ offsetY = other.offsetY;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -89,6 +106,28 @@
}
@Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final MagnificationSpec s = (MagnificationSpec) other;
+ return scale == s.scale && offsetX == s.offsetX && offsetY == s.offsetY;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (scale != +0.0f ? Float.floatToIntBits(scale) : 0);
+ result = 31 * result + (offsetX != +0.0f ? Float.floatToIntBits(offsetX) : 0);
+ result = 31 * result + (offsetY != +0.0f ? Float.floatToIntBits(offsetY) : 0);
+ return result;
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("<scale:");
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 88e9a95..b61706e 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -24,6 +24,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
@@ -142,6 +143,10 @@
private Bitmap mBitmap;
private float mHotSpotX;
private float mHotSpotY;
+ // The bitmaps for the additional frame of animated pointer icon. Note that the first frame
+ // will be stored in mBitmap.
+ private Bitmap mBitmapFrames[];
+ private int mDurationPerFrame;
private PointerIcon(int style) {
mStyle = style;
@@ -472,6 +477,36 @@
} else {
drawable = context.getDrawable(bitmapRes);
}
+ if (drawable instanceof AnimationDrawable) {
+ // Extract animation frame bitmaps.
+ final AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
+ final int frames = animationDrawable.getNumberOfFrames();
+ drawable = animationDrawable.getFrame(0);
+ if (frames == 1) {
+ Log.w(TAG, "Animation icon with single frame -- simply treating the first "
+ + "frame as a normal bitmap icon.");
+ } else {
+ // Assumes they have the exact duration.
+ mDurationPerFrame = animationDrawable.getDuration(0);
+ mBitmapFrames = new Bitmap[frames - 1];
+ final int width = drawable.getIntrinsicWidth();
+ final int height = drawable.getIntrinsicHeight();
+ for (int i = 1; i < frames; ++i) {
+ Drawable drawableFrame = animationDrawable.getFrame(i);
+ if (!(drawableFrame instanceof BitmapDrawable)) {
+ throw new IllegalArgumentException("Frame of an animated pointer icon "
+ + "must refer to a bitmap drawable.");
+ }
+ if (drawableFrame.getIntrinsicWidth() != width ||
+ drawableFrame.getIntrinsicHeight() != height) {
+ throw new IllegalArgumentException("The bitmap size of " + i + "-th frame "
+ + "is different. All frames should have the exact same size and "
+ + "share the same hotspot.");
+ }
+ mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+ }
+ }
+ }
if (!(drawable instanceof BitmapDrawable)) {
throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
+ "refer to a bitmap drawable.");
@@ -509,8 +544,7 @@
case STYLE_HELP:
return com.android.internal.R.styleable.Pointer_pointerIconHelp;
case STYLE_WAIT:
- // falls back to the default icon because no animation support.
- return com.android.internal.R.styleable.Pointer_pointerIconArrow;
+ return com.android.internal.R.styleable.Pointer_pointerIconWait;
case STYLE_CELL:
return com.android.internal.R.styleable.Pointer_pointerIconCell;
case STYLE_CROSSHAIR:
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 66b05a2..ab1943c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -83,6 +83,7 @@
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
+import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -806,6 +807,12 @@
private static boolean sAlwaysRemeasureExactly = false;
/**
+ * Relax constraints around whether setLayoutParams() must be called after
+ * modifying the layout params.
+ */
+ private static boolean sLayoutParamsAlwaysChanged = false;
+
+ /**
* This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
* calling setFlags.
*/
@@ -2416,6 +2423,8 @@
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
* 1111111 PFLAG3_POINTER_ICON_MASK
+ * 1 PFLAG3_PARTIAL_LAYOUT_REQUESTED
+ * 1 PFLAG3_LAYOUT_PARAMS_CHANGED
* |-------|-------|-------|-------|
*/
@@ -2504,6 +2513,7 @@
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
+
/* End of masks for mPrivateFlags3 */
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
@@ -2642,6 +2652,19 @@
private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
/**
+ * Flag indicating that this view has requested a partial layout and
+ * is added to the AttachInfo's list of views that need a partial layout
+ * request handled on the next traversal.
+ */
+ static final int PFLAG3_PARTIAL_LAYOUT_REQUESTED = 0x800000;
+
+ /**
+ * Flag indicating that this view's LayoutParams have been explicitly changed
+ * since the last layout pass.
+ */
+ static final int PFLAG3_LAYOUT_PARAMS_CHANGED = 0x1000000;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3679,7 +3702,7 @@
/**
* Flag indicating that a drag can cross window boundaries. When
- * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
+ * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, all visible applications will be able to participate
* in the drag operation and receive the dragged content.
*
@@ -3724,7 +3747,7 @@
/**
* Flag indicating that the drag shadow will be opaque. When
- * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
+ * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
* with this flag set, the drag shadow will be opaque, otherwise, it will be semitransparent.
*/
public static final int DRAG_FLAG_OPAQUE = 1 << 9;
@@ -3958,6 +3981,11 @@
// modes, so we always need to run an additional EXACTLY pass.
sAlwaysRemeasureExactly = targetSdkVersion <= M;
+ // Prior to N, layout params could change without requiring a
+ // subsequent call to setLayoutParams() and they would usually
+ // work. Partial layout breaks this assumption.
+ sLayoutParamsAlwaysChanged = targetSdkVersion <= M;
+
sCompatibilityDone = true;
}
}
@@ -6329,8 +6357,8 @@
position.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
- outRect.set((int) (position.left + 0.5f), (int) (position.top + 0.5f),
- (int) (position.right + 0.5f), (int) (position.bottom + 0.5f));
+ outRect.set(Math.round(position.left), Math.round(position.top),
+ Math.round(position.right), Math.round(position.bottom));
}
/**
@@ -12622,10 +12650,14 @@
* ViewGroup.LayoutParams, and these correspond to the different subclasses
* of ViewGroup that are responsible for arranging their children.
*
- * This method may return null if this View is not attached to a parent
+ * <p>This method may return null if this View is not attached to a parent
* ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)}
* was not invoked successfully. When a View is attached to a parent
- * ViewGroup, this method must not return null.
+ * ViewGroup, this method must not return null.</p>
+ *
+ * <p>Callers that modify the returned LayoutParams object should call
+ * {@link #setLayoutParams(LayoutParams)} to explicitly inform the view that
+ * LayoutParams have changed.</p>
*
* @return The LayoutParams associated with this view, or null if no
* parameters have been set yet
@@ -12642,6 +12674,9 @@
* correspond to the different subclasses of ViewGroup that are responsible
* for arranging their children.
*
+ * <p>If the View's existing LayoutParams object as obtained by {@link #getLayoutParams()} is
+ * modified, you should call this method to inform the view that it has changed.</p>
+ *
* @param params The layout parameters for this view, cannot be null
*/
public void setLayoutParams(ViewGroup.LayoutParams params) {
@@ -12649,6 +12684,7 @@
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
+ mPrivateFlags3 |= PFLAG3_LAYOUT_PARAMS_CHANGED;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
@@ -12968,10 +13004,6 @@
mPrivateFlags |= PFLAG_DIRTY;
- // Release any resources in-case we don't end up drawing again
- // as anything cached is no longer valid
- resetDisplayList();
-
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
@@ -14328,7 +14360,12 @@
mParent.requestTransparentRegion(this);
}
- mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
+ if ((mPrivateFlags & PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH) != 0) {
+ initialAwakenScrollBars();
+ mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
+ }
+
+ mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED);
jumpDrawablesToCurrentState();
@@ -14666,8 +14703,13 @@
*/
@CallSuper
protected void onDetachedFromWindowInternal() {
+ if (mAttachInfo != null && isPartialLayoutRequested()) {
+ mAttachInfo.mPartialLayoutViews.remove(this);
+ }
+
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
- mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
+ mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED
+ | PFLAG3_LAYOUT_PARAMS_CHANGED);
removeUnsetPressCallback();
removeLongPressCallback();
@@ -16854,6 +16896,32 @@
}
/**
+ * Indicates whether or not this view has requested a partial layout that
+ * may not affect its size or position within its parent. This state will be reset
+ * the next time this view is laid out.
+ *
+ * @return true if partial layout has been requested
+ */
+ public final boolean isPartialLayoutRequested() {
+ return (mPrivateFlags3 & PFLAG3_PARTIAL_LAYOUT_REQUESTED)
+ == PFLAG3_PARTIAL_LAYOUT_REQUESTED;
+ }
+
+ /**
+ * Returns true if this view's {@link ViewGroup.LayoutParams LayoutParams} changed
+ * since the last time this view was successfully laid out. Typically this happens as a
+ * result of a call to {@link #setLayoutParams(LayoutParams)}.
+ *
+ * @return true if this view's LayoutParams changed since last layout.
+ */
+ public final boolean didLayoutParamsChange() {
+ if (sLayoutParamsAlwaysChanged) {
+ return true;
+ }
+ return (mPrivateFlags3 & PFLAG3_LAYOUT_PARAMS_CHANGED) == PFLAG3_LAYOUT_PARAMS_CHANGED;
+ }
+
+ /**
* Return true if o is a ViewGroup that is laying out using optical bounds.
* @hide
*/
@@ -16910,6 +16978,7 @@
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
+ mPrivateFlags3 &= ~PFLAG3_LAYOUT_PARAMS_CHANGED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
@@ -16923,6 +16992,7 @@
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
+ mPrivateFlags3 &= ~PFLAG3_PARTIAL_LAYOUT_REQUESTED;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
@@ -18552,8 +18622,8 @@
position[1] -= vr.mCurScrollY;
}
- inOutLocation[0] = (int) (position[0] + 0.5f);
- inOutLocation[1] = (int) (position[1] + 0.5f);
+ inOutLocation[0] = Math.round(position[0]);
+ inOutLocation[1] = Math.round(position[1]);
}
/**
@@ -19016,7 +19086,7 @@
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
- mParent.requestLayout();
+ mParent.requestLayoutForChild(this);
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
@@ -19035,6 +19105,11 @@
mPrivateFlags |= PFLAG_INVALIDATED;
}
+ void forcePartialLayout() {
+ forceLayout();
+ mPrivateFlags3 |= PFLAG3_PARTIAL_LAYOUT_REQUESTED;
+ }
+
/**
* <p>
* This is called to find out how big a view should be. The parent
@@ -19849,6 +19924,15 @@
}
/**
+ * @deprecated Use {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)
+ * startDragAndDrop()} for newer platform versions.
+ */
+ public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder,
+ Object myLocalState, int flags) {
+ return startDragAndDrop(data, shadowBuilder, myLocalState, flags);
+ }
+
+ /**
* Starts a drag and drop operation. When your application calls this method, it passes a
* {@link android.view.View.DragShadowBuilder} object to the system. The
* system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)}
@@ -19865,9 +19949,10 @@
* {@link android.view.DragEvent#ACTION_DRAG_STARTED}.
* </p>
* <p>
- * Your application can invoke startDrag() on any attached View object. The View object does not
- * need to be the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to
- * be related to the View the user selected for dragging.
+ * Your application can invoke {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object,
+ * int) startDragAndDrop()} on any attached View object. The View object does not need to be
+ * the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to be related
+ * to the View the user selected for dragging.
* </p>
* @param data A {@link android.content.ClipData} object pointing to the data to be
* transferred by the drag and drop operation.
@@ -19887,10 +19972,10 @@
* {@code false} if it fails anywhere. Returning {@code false} means the system was unable to
* do a drag, and so no drag operation is in progress.
*/
- public final boolean startDrag(ClipData data, DragShadowBuilder shadowBuilder,
+ public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
Object myLocalState, int flags) {
if (ViewDebug.DEBUG_DRAG) {
- Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " flags=" + flags);
+ Log.d(VIEW_LOG_TAG, "startDragAndDrop: data=" + data + " flags=" + flags);
}
boolean okay = false;
@@ -19907,19 +19992,22 @@
Log.d(VIEW_LOG_TAG, "drag shadow: width=" + shadowSize.x + " height=" + shadowSize.y
+ " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
}
- Surface surface = new Surface();
+ if (mAttachInfo.mDragSurface != null) {
+ mAttachInfo.mDragSurface.release();
+ }
+ mAttachInfo.mDragSurface = new Surface();
try {
- IBinder token = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow,
- flags, shadowSize.x, shadowSize.y, surface);
- if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" + token
- + " surface=" + surface);
- if (token != null) {
- Canvas canvas = surface.lockCanvas(null);
+ mAttachInfo.mDragToken = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow,
+ flags, shadowSize.x, shadowSize.y, mAttachInfo.mDragSurface);
+ if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token="
+ + mAttachInfo.mDragToken + " surface=" + mAttachInfo.mDragSurface);
+ if (mAttachInfo.mDragToken != null) {
+ Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null);
try {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
shadowBuilder.onDrawShadow(canvas);
} finally {
- surface.unlockCanvasAndPost(canvas);
+ mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas);
}
final ViewRootImpl root = getViewRootImpl();
@@ -19930,24 +20018,75 @@
// repurpose 'shadowSize' for the last touch point
root.getLastTouchPoint(shadowSize);
- okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token,
+ okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, mAttachInfo.mDragToken,
shadowSize.x, shadowSize.y,
shadowTouchPoint.x, shadowTouchPoint.y, data);
if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay);
-
- // Off and running! Release our local surface instance; the drag
- // shadow surface is now managed by the system process.
- surface.release();
}
} catch (Exception e) {
Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
- surface.destroy();
+ mAttachInfo.mDragSurface.destroy();
+ mAttachInfo.mDragSurface = null;
}
return okay;
}
/**
+ * Cancels an ongoing drag and drop operation.
+ * <p>
+ * A {@link android.view.DragEvent} object with
+ * {@link android.view.DragEvent#getAction()} value of
+ * {@link android.view.DragEvent#ACTION_DRAG_ENDED} and
+ * {@link android.view.DragEvent#getResult()} value of {@code false}
+ * will be sent to every
+ * View that received {@link android.view.DragEvent#ACTION_DRAG_STARTED}
+ * even if they are not currently visible.
+ * </p>
+ * <p>
+ * This method can be called on any View in the same window as the View on which
+ * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int) startDragAndDrop}
+ * was called.
+ * </p>
+ */
+ public final void cancelDragAndDrop() {
+ if (ViewDebug.DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "cancelDragAndDrop");
+ }
+ if (mAttachInfo.mDragToken != null) {
+ try {
+ mAttachInfo.mSession.cancelDragAndDrop(mAttachInfo.mDragToken);
+ } catch (Exception e) {
+ Log.e(VIEW_LOG_TAG, "Unable to cancel drag", e);
+ }
+ mAttachInfo.mDragToken = null;
+ } else {
+ Log.e(VIEW_LOG_TAG, "No active drag to cancel");
+ }
+ }
+
+ public final void updateDragShadow(DragShadowBuilder shadowBuilder) {
+ if (ViewDebug.DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "updateDragShadow");
+ }
+ if (mAttachInfo.mDragToken != null) {
+ try {
+ Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null);
+ try {
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ shadowBuilder.onDrawShadow(canvas);
+ } finally {
+ mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas);
+ }
+ } catch (Exception e) {
+ Log.e(VIEW_LOG_TAG, "Unable to update drag shadow", e);
+ }
+ } else {
+ Log.e(VIEW_LOG_TAG, "No active drag");
+ }
+ }
+
+ /**
* Starts a move from {startX, startY}, the amount of the movement will be the offset
* between {startX, startY} and the new cursor positon.
* @param startX horizontal coordinate where the move started.
@@ -19969,7 +20108,8 @@
/**
* Handles drag events sent by the system following a call to
- * {@link android.view.View#startDrag(ClipData,DragShadowBuilder,Object,int) startDrag()}.
+ * {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int)
+ * startDragAndDrop()}.
*<p>
* When the system calls this method, it passes a
* {@link android.view.DragEvent} object. A call to
@@ -21855,6 +21995,7 @@
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
+ void schedulePartialLayout();
}
/**
@@ -22225,6 +22366,22 @@
View mViewRequestingLayout;
/**
+ * Used to track views that need (at least) a partial relayout at their current size
+ * during the next traversal.
+ */
+ final List<View> mPartialLayoutViews = new ArrayList<View>();
+
+ /**
+ * Used to track the identity of the current drag operation.
+ */
+ IBinder mDragToken;
+
+ /**
+ * The drag shadow surface for the current drag operation.
+ */
+ public Surface mDragSurface;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index db978a6..11df9a3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -60,6 +60,8 @@
import java.util.List;
import java.util.Map;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* <p>
@@ -222,7 +224,7 @@
* NOTE: If you change the flags below make sure to reflect the changes
* the DisplayList class
*/
-
+
// When set, ViewGroup invalidates only the child's rectangle
// Set by default
static final int FLAG_CLIP_CHILDREN = 0x1;
@@ -267,7 +269,7 @@
/**
* When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
* to get the index of the child to draw for that iteration.
- *
+ *
* @hide
*/
protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
@@ -1325,7 +1327,7 @@
children[i].dispatchConfigurationChanged(newConfig);
}
}
-
+
/**
* {@inheritDoc}
*/
@@ -2212,7 +2214,7 @@
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
- final ArrayList<View> preorderedList = buildOrderedChildList();
+ final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
@@ -2345,6 +2347,18 @@
}
/**
+ * Provide custom ordering of views in which the touch will be dispatched.
+ *
+ * This is called within a tight loop, so you are not allowed to allocate objects, including
+ * the return array. Instead, you should return a pre-allocated list that will be cleared
+ * after the dispatch is finished.
+ * @hide
+ */
+ public ArrayList<View> buildTouchDispatchChildList() {
+ return buildOrderedChildList();
+ }
+
+ /**
* Finds the child which has accessibility focus.
*
* @return The child that has focus.
@@ -2785,7 +2799,7 @@
* @see #FOCUS_BEFORE_DESCENDANTS
* @see #FOCUS_AFTER_DESCENDANTS
* @see #FOCUS_BLOCK_DESCENDANTS
- * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
+ * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
*/
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
@@ -4102,7 +4116,7 @@
/**
* <p>Adds a child view. If no layout parameters are already set on the child, the
* default parameters for this ViewGroup are set on the child.</p>
- *
+ *
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
@@ -4118,7 +4132,7 @@
/**
* Adds a child view. If no layout parameters are already set on the child, the
* default parameters for this ViewGroup are set on the child.
- *
+ *
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
@@ -4558,7 +4572,7 @@
/**
* {@inheritDoc}
- *
+ *
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
@@ -4577,7 +4591,7 @@
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
- *
+ *
* @param view the view to remove from the group
*/
public void removeViewInLayout(View view) {
@@ -4605,7 +4619,7 @@
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
- *
+ *
* @param index the position in the group of the view to remove
*/
public void removeViewAt(int index) {
@@ -4794,7 +4808,7 @@
/**
* Call this method to remove all child views from the
* ViewGroup.
- *
+ *
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
@@ -5114,10 +5128,10 @@
transformMatrix = childMatrix;
}
transformMatrix.mapRect(boundingRect);
- dirty.set((int) (boundingRect.left - 0.5f),
- (int) (boundingRect.top - 0.5f),
- (int) (boundingRect.right + 0.5f),
- (int) (boundingRect.bottom + 0.5f));
+ dirty.set((int) Math.floor(boundingRect.left),
+ (int) Math.floor(boundingRect.top),
+ (int) Math.ceil(boundingRect.right),
+ (int) Math.ceil(boundingRect.bottom));
}
do {
@@ -5154,10 +5168,10 @@
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
- dirty.set((int) (boundingRect.left - 0.5f),
- (int) (boundingRect.top - 0.5f),
- (int) (boundingRect.right + 0.5f),
- (int) (boundingRect.bottom + 0.5f));
+ dirty.set((int) Math.floor(boundingRect.left),
+ (int) Math.floor(boundingRect.top),
+ (int) Math.ceil(boundingRect.right),
+ (int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
@@ -5457,8 +5471,8 @@
position[0] = offset.x;
position[1] = offset.y;
child.getMatrix().mapPoints(position);
- offset.x = (int) (position[0] + 0.5f);
- offset.y = (int) (position[1] + 0.5f);
+ offset.x = Math.round(position[0]);
+ offset.y = Math.round(position[1]);
}
offset.x += dx;
offset.y += dy;
@@ -5485,8 +5499,8 @@
rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
mClipBounds.bottom);
}
- r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f), (int) (rect.right + 0.5f),
- (int) (rect.bottom + 0.5f));
+ r.set((int) Math.floor(rect.left), (int) Math.floor(rect.top),
+ (int) Math.ceil(rect.right), (int) Math.ceil(rect.bottom));
if (rectIsVisible && mParent != null) {
rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
}
@@ -5517,6 +5531,172 @@
int l, int t, int r, int b);
/**
+ * {@inheritDoc}
+ *
+ * <p>Most subclasses should not need to override this method. The default implementation
+ * will call {@link #findDependentLayoutAxes(View, int)} to determine how
+ * to optimally proceed. If neither horizontal nor vertical layout depends on the given
+ * child, this method will call {@link #requestPartialLayoutForChild(View)}. If one or both
+ * do, it will call {@link #requestLayout()}.</p>
+ *
+ * @param child Child requesting a layout
+ */
+ @Override
+ public void requestLayoutForChild(View child) {
+ if (child == null || child.getParent() != this) {
+ throw new IllegalArgumentException(
+ "child parameter must be a direct child view of this ViewGroup");
+ }
+
+ // If we don't have a parent ourselves, record that we need a full layout.
+ // Our whole subtree is detached.
+ final ViewParent parent = getParent();
+ if (parent == null) {
+ requestLayout();
+ return;
+ }
+
+ // We can optimize the layout request for this child into a partial layout
+ // if the child has already been laid out at least once and neither horizontal nor
+ // vertical layout within ourselves is dependent on pending layout changes within
+ // this child. Otherwise we need to request a full layout for ourselves and continue
+ // to recurse up the view hierarchy.
+ if (child.isLaidOut() && findDependentLayoutAxes(child, FLAG_LAYOUT_AXIS_ANY) == 0) {
+ requestPartialLayoutForChild(child);
+ } else {
+ requestLayout();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation returns {@link #FLAG_LAYOUT_AXIS_ANY}.
+ * Optimized implementations for specific ViewGroup subclasses may check if the child's
+ * {@link View#didLayoutParamsChange() LayoutParams changed} and in what ways.</p>
+ *
+ * @param child Direct child of this ViewParent to check
+ * @param axisFilter Which axes to check for dependencies. Can be
+ * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
+ * or {@link #FLAG_LAYOUT_AXIS_ANY}.
+ * @return Axes of this ViewParent that depend on the given child's layout changes
+ */
+ @Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ return FLAG_LAYOUT_AXIS_ANY;
+ }
+
+ /**
+ * This is a helper implementation for {@link #findDependentLayoutAxes(View, int)} that
+ * is not the default implementation in ViewGroup. This is to preserve compatibility with
+ * existing app-side ViewGroup subclasses that existed before the partial layout system was
+ * added to Android. It explicitly checks that the LayoutParams of the child are of the
+ * expected type so that subclasses of standard framework layouts do not erroneously
+ * start believing that it's safe to do a partial layout when that assertion can't
+ * reasonably be confirmed.
+ *
+ * <p>If you're reading this as an author of a custom ViewGroup's findDependentLayoutAxes
+ * method you might be frustrated to discover that it is not a part of the Android public API.
+ * Many ViewGroup implementations will need to make small but important modifications
+ * to an implementation like this one in order to be correct. Instead of encouraging
+ * view authors to call this method, then make their own redundant recursive calls to
+ * <code>getParent().findDependentLayoutAxes(...)</code> in addition to the one
+ * that can happen here, this method is hidden and only used internally.</p>
+ *
+ * <p>Do feel free to copy this implementation and adapt it to suit your own purposes.</p>
+ *
+ * @hide
+ */
+ protected final int findDependentLayoutAxesHelper(View child, int axisFilter,
+ Class<?> layoutParamsClass) {
+ if (!checkPartialLayoutParams(child, layoutParamsClass)) return axisFilter;
+ if (child.didLayoutParamsChange()) {
+ // Anything could have changed about our previous assumptions.
+ return axisFilter;
+ }
+
+ final LayoutParams lp = child.getLayoutParams();
+
+ // Our layout can always end up depending on a WRAP_CONTENT child.
+ final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
+ | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
+
+ if (wrapAxisFilter == axisFilter) {
+ // We know all queried axes are affected, just return early.
+ return wrapAxisFilter;
+ }
+
+ // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
+ // that our layout will remain stable within our parent. We need to ask.
+ final int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
+ | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
+
+ if (matchAxisFilter != 0) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ // If our parent depends on us for an axis, then our layout can also be affected
+ // by a MATCH_PARENT child along that axis.
+ return getParent().findDependentLayoutAxes(this, matchAxisFilter)
+ | wrapAxisFilter;
+ }
+
+ // If we don't have a parent, assume we're affected
+ // in any determined affected direction.
+ return matchAxisFilter | wrapAxisFilter;
+ }
+
+ // Two exact sizes and LayoutParams didn't change. We're safe.
+ return 0;
+ }
+
+ /**
+ * Throw an IllegalArgumentException if the supplied view is not a direct child of
+ * this ViewGroup and return false if this view's LayoutParams is not of class lpClass.
+ * Implementations of {@link ViewGroup#findDependentLayoutAxes(View, int)} use this
+ * to check input parameters and defensively return the full axis filter mask themselves
+ * if the LayoutParams class is not of the exact expected type; e.g. it is a subclass
+ * of one of the standard framework layouts and we can't make assumptions.
+ * @hide
+ */
+ protected final boolean checkPartialLayoutParams(View child, Class<?> lpClass) {
+ if (child.getParent() != this) {
+ throw new IllegalArgumentException("View " + child
+ + " is not a direct child of " + this);
+ }
+ final ViewGroup.LayoutParams lp = child.getLayoutParams();
+ return lp != null || lp.getClass() == lpClass;
+ }
+
+ /**
+ * Called when a child of this ViewParent requires a relayout before the next frame
+ * is drawn, but the caller can guarantee that the size of the child will not change
+ * during a measure and layout pass.
+ *
+ * <p>A call to this method will schedule a partial layout for the supplied view as long as
+ * it is a direct child of this ViewGroup and this ViewGroup is attached to a window.
+ * On the next scheduled view hierarchy traversal the given child view will be re-measured
+ * at its current measured size and re-laid out at its current position within its parent.</p>
+ *
+ * @param child Child that requires a partial layout
+ */
+ public void requestPartialLayoutForChild(View child) {
+ if (!child.isPartialLayoutRequested()) {
+ child.forcePartialLayout();
+ if (mAttachInfo != null) {
+ final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
+ final boolean schedule = partialLayoutViews.isEmpty();
+ partialLayoutViews.add(child);
+ if (schedule) {
+ mAttachInfo.mRootCallbacks.schedulePartialLayout();
+ }
+ child.invalidate();
+ } else {
+ requestLayout();
+ }
+ }
+ }
+
+ /**
* Indicates whether the view group has the ability to animate its children
* after the first layout.
*
@@ -5862,7 +6042,7 @@
* of its descendants
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return p;
+ return new LayoutParams(p);
}
/**
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 07f1e2c..6ae448a 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -26,6 +26,11 @@
*
*/
public interface ViewParent {
+ public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1;
+ public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2;
+ public static final int FLAG_LAYOUT_AXIS_ANY
+ = FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL;
+
/**
* Called when something has changed which has invalidated the layout of a
* child of this view parent. This will schedule a layout pass of the view
@@ -601,4 +606,48 @@
* @return true if the action was consumed by this ViewParent
*/
public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
+
+ /**
+ * Called when a child of this ViewParent requires a relayout before
+ * the next frame is drawn. A call to {@link View#requestLayout() child.requestLayout()}
+ * will implicitly result in a call to
+ * <code>child.getParent().requestLayoutForChild(child)</code>. App code should not call this
+ * method directly. Call <code>child.requestLayout()</code> instead.
+ *
+ * <p>On versions of Android from API 23 and older, a call to {@link View#requestLayout()}
+ * would cause a matching call to <code>requestLayout</code> on each parent view up to
+ * the root. With the addition of <code>requestLayoutForChild</code> a view's parent may
+ * explicitly decide how to handle a layout request. This allows for optimizations when
+ * a view parent knows that a layout-altering change in a child will not affect its own
+ * measurement.</p>
+ *
+ * @param child Child requesting a layout
+ */
+ public void requestLayoutForChild(View child);
+
+ /**
+ * Determine which axes of this ViewParent's layout are dependent on the given
+ * direct child view. The returned value is a flag set that may contain
+ * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL} and/or {@link #FLAG_LAYOUT_AXIS_VERTICAL}.
+ * {@link #FLAG_LAYOUT_AXIS_ANY} is provided as a shortcut for
+ * <code>FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL</code>.
+ *
+ * <p>The given child must be a direct child view. Implementations should throw
+ * {@link IllegalArgumentException} otherwise.</p>
+ *
+ * <p>The caller may specify which axes it cares about. This should be treated as a filter.
+ * Implementations should never return a result that would be different from
+ * <code>result & axisFilter</code>.</p>
+ *
+ * @param child Direct child of this ViewParent to check
+ * @param axisFilter Which axes to check for dependencies. Can be
+ * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
+ * or {@link #FLAG_LAYOUT_AXIS_ANY}.
+ * @return Axes of this ViewParent that depend on the given child's layout changes
+ *
+ * @see #FLAG_LAYOUT_AXIS_HORIZONTAL
+ * @see #FLAG_LAYOUT_AXIS_VERTICAL
+ * @see #FLAG_LAYOUT_AXIS_ANY
+ */
+ public int findDependentLayoutAxes(View child, int axisFilter);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d80c6a3..b503e12 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+
import android.Manifest;
import android.animation.LayoutTransition;
import android.app.ActivityManagerNative;
@@ -37,7 +39,6 @@
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
@@ -74,7 +75,6 @@
import android.view.animation.Interpolator;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.WindowCallbacks;
import android.widget.Scroller;
import com.android.internal.R;
@@ -91,6 +91,7 @@
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.HashSet;
+import java.util.List;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -176,6 +177,11 @@
int mViewVisibility;
boolean mAppVisible = true;
+ // For recents to freeform transition we need to keep drawing after the app receives information
+ // that it became invisible. This will ignore that information and depend on the decor view
+ // visibility to control drawing. The decor view visibility will get adjusted when the app get
+ // stopped and that's when the app will stop drawing further frames.
+ private boolean mForceDecorViewVisibility = false;
int mOrigWindowType = -1;
/** Whether the window had focus during the most recent traversal. */
@@ -561,6 +567,8 @@
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
+ mForceDecorViewVisibility = (mWindowAttributes.privateFlags
+ & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
@@ -945,6 +953,25 @@
}
@Override
+ public void requestLayoutForChild(View child) {
+ requestLayout();
+ }
+
+ @Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ if (child != mView) {
+ return 0;
+ }
+
+ final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) child.getLayoutParams();
+ final int horizontal = (lp.width == WindowManager.LayoutParams.WRAP_CONTENT
+ || lp.horizontalWeight != 0) ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0;
+ final int vertical = (lp.height == WindowManager.LayoutParams.WRAP_CONTENT
+ || lp.verticalWeight != 0) ? FLAG_LAYOUT_AXIS_VERTICAL : 0;
+ return (horizontal | vertical) & axisFilter;
+ }
+
+ @Override
public boolean isLayoutRequested() {
return mLayoutRequested;
}
@@ -1063,7 +1090,7 @@
}
int getHostVisibility() {
- return mAppVisible ? mView.getVisibility() : View.GONE;
+ return (mAppVisible || mForceDecorViewVisibility) ? mView.getVisibility() : View.GONE;
}
/**
@@ -1088,6 +1115,10 @@
}
}
+ public void schedulePartialLayout() {
+ scheduleTraversals();
+ }
+
/**
* Notifies the HardwareRenderer that a new frame will be coming soon.
* Currently only {@link ThreadedRenderer} cares about this, and uses
@@ -1568,7 +1599,7 @@
boolean insetsPending = false;
int relayoutResult = 0;
- boolean isViewVisible = viewVisibility == View.VISIBLE;
+ final boolean isViewVisible = viewVisibility == View.VISIBLE;
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
@@ -1625,7 +1656,7 @@
if (mPendingConfiguration.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+ mPendingConfiguration);
- updateConfiguration(mPendingConfiguration, !mFirst);
+ updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
mPendingConfiguration.seq = 0;
}
@@ -1638,6 +1669,8 @@
final boolean stableInsetsChanged = !mPendingStableInsets.equals(
mAttachInfo.mStableInsets);
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
+ final boolean surfaceSizeChanged = (relayoutResult
+ & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
if (contentInsetsChanged) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
@@ -1723,10 +1756,20 @@
mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.destroy();
}
- } else if (surfaceGenerationId != mSurface.getGenerationId() &&
- mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
+ } else if ((surfaceGenerationId != mSurface.getGenerationId()
+ || surfaceSizeChanged)
+ && mSurfaceHolder == null
+ && mAttachInfo.mHardwareRenderer != null) {
mFullRedrawNeeded = true;
try {
+ // Need to do updateSurface (which leads to CanvasContext::setSurface and
+ // re-create the EGLSurface) if either the Surface changed (as indicated by
+ // generation id), or WindowManager changed the surface size. The latter is
+ // because on some chips, changing the consumer side's BufferQueue size may
+ // not take effect immediately unless we create a new EGLSurface.
+ // Note that frame size change doesn't always imply surface size change (eg.
+ // drag resizing uses fullscreen surface), need to check surfaceSizeChanged
+ // flag from WindowManager.
mAttachInfo.mHardwareRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
@@ -1915,7 +1958,48 @@
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
+ }
+ /*
+ * Handle partial layouts.
+ *
+ * Views that have requested partial layouts will not change size or position
+ * within their parent view, therefore we will re-measure and re-layout each one
+ * after any regularly scheduled layout pass. Any view that already had its
+ * isLayoutRequested bit cleared will be skipped, since this means the view has already
+ * been measured and laid out on this traversal pass naturally. Views won't be added
+ * to this list if layout was already requested when a partial layout is requested
+ * for a view, so there should not be duplicates in the list.
+ */
+ final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
+ final boolean didPartialLayout;
+ if (!partialLayoutViews.isEmpty()) {
+ final int count = partialLayoutViews.size();
+ mInLayout = true;
+ for (int i = 0; i < count; i++) {
+ final View view = partialLayoutViews.get(i);
+
+ // Make sure the view is still attached and that it still has layout requested.
+ // We might have already serviced the layout request through the standard full-tree
+ // layout pass above or even through a previous partial layout view in this list.
+ if (view.isAttachedToWindow() && view.isLayoutRequested()) {
+ final int widthSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(),
+ MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(),
+ MeasureSpec.EXACTLY);
+ view.measure(widthSpec, heightSpec);
+ view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ }
+ }
+ mInLayout = false;
+ partialLayoutViews.clear();
+ didPartialLayout = true;
+ triggerGlobalLayoutListener = true;
+ } else {
+ didPartialLayout = false;
+ }
+
+ if (didLayout || didPartialLayout) {
// By this point all views have been sized and positioned
// We can compute the transparent area
@@ -1945,7 +2029,7 @@
if (DBG) {
System.out.println("======================================");
- System.out.println("performTraversals -- after setFrame");
+ System.out.println("performTraversals -- after performLayout/partial layout");
host.debug();
}
}
@@ -2459,8 +2543,7 @@
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
- ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
- mSurfaceHolder);
+ ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder);
}
}
}
@@ -5289,10 +5372,14 @@
}
}
- // When the drag operation ends, release any local state object
- // that may have been in use
+ // When the drag operation ends, reset drag-related state
if (what == DragEvent.ACTION_DRAG_ENDED) {
setLocalDragState(null);
+ mAttachInfo.mDragToken = null;
+ if (mAttachInfo.mDragSurface != null) {
+ mAttachInfo.mDragSurface.release();
+ mAttachInfo.mDragSurface = null;
+ }
}
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 8259372..3c4d45a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -276,6 +276,8 @@
private boolean mDestroyed;
+ private boolean mOverlayWithDecorCaption = false;
+
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
@@ -2044,4 +2046,18 @@
/** @hide */
public void setTheme(int resId) {
}
+
+ /**
+ * Whether the caption should be displayed directly on the content rather than push the content
+ * down. This affects only freeform windows since they display the caption.
+ * @hide
+ */
+ public void setOverlayDecorCaption(boolean overlayCaption) {
+ mOverlayWithDecorCaption = overlayCaption;
+ }
+
+ /** @hide */
+ public boolean getOverlayDecorCaption() {
+ return mOverlayWithDecorCaption;
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index edf4297..1521f2e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -118,8 +118,7 @@
*/
public void removeViewImmediate(View view);
- public static class LayoutParams extends ViewGroup.LayoutParams
- implements Parcelable {
+ public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
* When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or
@@ -1149,13 +1148,21 @@
/**
* Flag indicating that the x, y, width, and height members should be
- * ignored (and thus their previous value preserved). For example
+ * ignored (and thus their previous value preserved). For example
* because they are being managed externally through repositionChild.
*
* {@hide}
*/
public static final int PRIVATE_FLAG_PRESERVE_GEOMETRY = 0x00002000;
+ /**
+ * Flag that will make window ignore app visibility and instead depend purely on the decor
+ * view visibility for determining window visibility. This is used by recents to keep
+ * drawing after it launches an app.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000;
+
/**
* Control flags that are private to the platform.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 43d643e..c08e1b5 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -77,6 +77,11 @@
public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
/**
+ * The window manager has changed the size of the surface from the last call.
+ */
+ public static final int RELAYOUT_RES_SURFACE_RESIZED = 0x10;
+
+ /**
* Flag for relayout: the client will be later giving
* internal insets; as a result, the window will not impact other window
* layouts until the insets are given.
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 3882bca..89b1eb9 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -55,9 +56,10 @@
* Called when the bounds of the screen content that is magnified changed.
* Note that not the entire screen is magnified.
*
- * @param bounds The bounds.
+ * @param magnifiedBounds the currently magnified region
+ * @param availableBounds the region available for magnification
*/
- public void onMagnifedBoundsChanged(Region bounds);
+ public void onMagnifiedBoundsChanged(Region magnifiedBounds, Region availableBounds);
/**
* Called when an application requests a rectangle on the screen to allow
@@ -142,7 +144,7 @@
*
* @param callbacks The callbacks to invoke.
*/
- public abstract void setMagnificationCallbacks(MagnificationCallbacks callbacks);
+ public abstract void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks);
/**
* Set by the accessibility layer to specify the magnification and panning to
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ee06806..19a98f3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -23,6 +23,7 @@
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
+import com.android.internal.view.InputMethodClient;
import android.annotation.RequiresPermission;
import android.content.Context;
@@ -440,33 +441,24 @@
}
case MSG_UNBIND: {
final int sequence = msg.arg1;
+ @InputMethodClient.UnbindReason
+ final int reason = msg.arg2;
if (DEBUG) {
- Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence);
+ Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
+ " reason=" + InputMethodClient.getUnbindReason(reason));
}
- boolean startInput = false;
+ final boolean startInput;
synchronized (mH) {
- if (mBindSequence == sequence) {
- if (false) {
- // XXX the server has already unbound!
- if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
- try {
- mCurMethod.finishInput();
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId, e);
- }
- }
- }
- clearBindingLocked();
-
- // If we were actively using the last input method, then
- // we would like to re-connect to the next input method.
- if (mServedView != null && mServedView.isFocused()) {
- mServedConnecting = true;
- }
- if (mActive) {
- startInput = true;
- }
+ if (mBindSequence != sequence) {
+ return;
}
+ clearBindingLocked();
+ // If we were actively using the last input method, then
+ // we would like to re-connect to the next input method.
+ if (mServedView != null && mServedView.isFocused()) {
+ mServedConnecting = true;
+ }
+ startInput = mActive;
}
if (startInput) {
startInputInner(null, 0, 0, 0);
@@ -548,6 +540,13 @@
void deactivate() {
mActive = false;
}
+
+ @Override
+ public String toString() {
+ return "ControlledInputConnectionWrapper{mActive=" + mActive
+ + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ + "}";
+ }
}
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@@ -581,8 +580,8 @@
}
@Override
- public void onUnbindMethod(int sequence) {
- mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
+ public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
+ mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, unbindReason));
}
@Override
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index b8faf0c..90de053 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2109,6 +2109,11 @@
}
@Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mSelector == null) {
useDefaultSelector();
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 5eea252..41f1ce7 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -177,8 +177,7 @@
public void onConfigurationChanged(Configuration newConfig) {
if (!mMaxItemsSet) {
- mMaxItems = mContext.getResources().getInteger(
- com.android.internal.R.integer.max_action_buttons);
+ mMaxItems = ActionBarPolicy.get(mContext).getMaxActionButtons();
}
if (mMenu != null) {
mMenu.onItemsChanged(true);
@@ -938,10 +937,11 @@
}
@Override
- public void onDismiss() {
- super.onDismiss();
+ protected void onDismiss() {
mMenu.close();
mOverflowPopup = null;
+
+ super.onDismiss();
}
}
@@ -960,10 +960,11 @@
}
@Override
- public void onDismiss() {
- super.onDismiss();
+ protected void onDismiss() {
mActionButtonPopup = null;
mOpenSubMenuId = 0;
+
+ super.onDismiss();
}
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index a018d26..9f94005 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -28,6 +28,8 @@
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.RemotableViewMethod;
@@ -447,6 +449,68 @@
return CheckedTextView.class.getName();
}
+ static class SavedState extends BaseSavedState {
+ boolean checked;
+
+ /**
+ * Constructor called from {@link CheckedTextView#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ checked = (Boolean)in.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeValue(checked);
+ }
+
+ @Override
+ public String toString() {
+ return "CheckedTextView.SavedState{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " checked=" + checked + "}";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ SavedState ss = new SavedState(superState);
+
+ ss.checked = isChecked();
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+
+ super.onRestoreInstanceState(ss.getSuperState());
+ setChecked(ss.checked);
+ requestLayout();
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 7e542c9..cbb6109 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -171,7 +171,7 @@
// Set up year picker view.
mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
- mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
// Set up content descriptions.
@@ -267,6 +267,9 @@
// Automatically switch to day picker.
setCurrentView(VIEW_MONTH_DAY);
+
+ // Switch focus back to the year text.
+ mHeaderYear.requestFocus();
}
};
@@ -344,7 +347,18 @@
mAnimator.announceForAccessibility(mSelectDay);
break;
case VIEW_YEAR:
- mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ final int year = mCurrentDate.get(Calendar.YEAR);
+ mYearPickerView.setYear(year);
+ mYearPickerView.post(new Runnable() {
+ @Override
+ public void run() {
+ mYearPickerView.requestFocus();
+ final View selected = mYearPickerView.getSelectedView();
+ if (selected != null) {
+ selected.requestFocus();
+ }
+ }
+ });
if (mCurrentView != viewIndex) {
mHeaderMonthDay.setActivated(false);
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 8fe8252..f5840dc 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -287,6 +287,14 @@
return null;
}
+ SimpleMonthView getView(Object object) {
+ if (object == null) {
+ return null;
+ }
+ final ViewHolder holder = (ViewHolder) object;
+ return holder.calendar;
+ }
+
private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
@Override
public void onDayClick(SimpleMonthView view, Calendar day) {
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
index bb6e3a4..5f0ae29 100644
--- a/core/java/android/widget/DayPickerViewPager.java
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -16,12 +16,18 @@
package android.widget;
-import com.android.internal.widget.ViewPager;
-
+import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import com.android.internal.util.Predicate;
+import com.android.internal.widget.PagerAdapter;
+import com.android.internal.widget.ViewPager;
import java.util.ArrayList;
@@ -134,4 +140,37 @@
mMatchParentChildren.clear();
}
+
+ @Override
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ if (predicate.apply(this)) {
+ return this;
+ }
+
+ // Always try the selected view first.
+ final DayPickerPagerAdapter adapter = (DayPickerPagerAdapter) getAdapter();
+ final SimpleMonthView current = adapter.getView(getCurrent());
+ if (current != childToSkip && current != null) {
+ final View v = current.findViewByPredicate(predicate);
+ if (v != null) {
+ return v;
+ }
+ }
+
+ final int len = getChildCount();
+ for (int i = 0; i < len; i++) {
+ final View child = getChildAt(i);
+
+ if (child != childToSkip && child != current) {
+ final View v = child.findViewByPredicate(predicate);
+
+ if (v != null) {
+ return v;
+ }
+ }
+ }
+
+ return null;
+ }
+
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index d21a5f7..e31bbe9 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -128,6 +128,15 @@
/** @hide */
@Override
+ public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoInternal(info);
+ if (isEnabled()) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
+ }
+ }
+
+ /** @hide */
+ @Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_SET_TEXT: {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 8cd4de3..c3d0993 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2634,8 +2634,11 @@
@VisibleForTesting
public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
- private static final int ADD_TO_DICTIONARY = -1;
- private static final int DELETE_TEXT = -2;
+
+ // Key of intent extras for inserting new word into user dictionary.
+ private static final String USER_DICTIONARY_EXTRA_WORD = "word";
+ private static final String USER_DICTIONARY_EXTRA_LOCALE = "locale";
+
private SuggestionInfo[] mSuggestionInfos;
private int mNumberOfSuggestions;
private boolean mCursorWasVisibleBeforeSuggestions;
@@ -2644,7 +2647,10 @@
private final Comparator<SuggestionSpan> mSuggestionSpanComparator;
private final HashMap<SuggestionSpan, Integer> mSpansLengths;
private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan(
- mTextView.getContext(), android.R.style.TextAppearance_SuggestionHighlight);
+ mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle);
+ private TextView mAddToDictionaryButton;
+ private TextView mDeleteButton;
+ private SuggestionSpan mMisspelledSpan;
private class CustomPopupWindow extends PopupWindow {
public CustomPopupWindow(Context context, int defStyleAttr) {
@@ -2684,17 +2690,73 @@
@Override
protected void initContentView() {
- ListView listView = new ListView(mTextView.getContext());
- mSuggestionsAdapter = new SuggestionAdapter();
- listView.setAdapter(mSuggestionsAdapter);
- listView.setOnItemClickListener(this);
- mContentView = listView;
+ final LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final LinearLayout linearLayout = (LinearLayout) inflater.inflate(
+ mTextView.mTextEditSuggestionContainerLayout, null);
- // Inflate the suggestion items once and for all. + 2 for add to dictionary and delete
- mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 2];
+ final ListView suggestionListView = (ListView) linearLayout.findViewById(
+ com.android.internal.R.id.suggestionContainer);
+
+ mSuggestionsAdapter = new SuggestionAdapter();
+ suggestionListView.setAdapter(mSuggestionsAdapter);
+ suggestionListView.setOnItemClickListener(this);
+
+ // Inflate the suggestion items once and for all.
+ mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS];
for (int i = 0; i < mSuggestionInfos.length; i++) {
mSuggestionInfos[i] = new SuggestionInfo();
}
+
+ mContentView = linearLayout;
+
+ mAddToDictionaryButton = (TextView) linearLayout.findViewById(
+ com.android.internal.R.id.addToDictionaryButton);
+ mAddToDictionaryButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ final Editable editable = (Editable) mTextView.getText();
+ final int spanStart = editable.getSpanStart(mMisspelledSpan);
+ final int spanEnd = editable.getSpanEnd(mMisspelledSpan);
+ final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
+
+ final Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
+ intent.putExtra(USER_DICTIONARY_EXTRA_WORD, originalText);
+ intent.putExtra(USER_DICTIONARY_EXTRA_LOCALE,
+ mTextView.getTextServicesLocale().toString());
+ intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+ mTextView.getContext().startActivity(intent);
+ // There is no way to know if the word was indeed added. Re-check.
+ // TODO The ExtractEditText should remove the span in the original text instead
+ editable.removeSpan(mMisspelledSpan);
+ Selection.setSelection(editable, spanEnd);
+ updateSpellCheckSpans(spanStart, spanEnd, false);
+ hideWithCleanUp();
+ }
+ });
+
+ mDeleteButton = (TextView) linearLayout.findViewById(
+ com.android.internal.R.id.deleteButton);
+ mDeleteButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ final Editable editable = (Editable) mTextView.getText();
+
+ final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan);
+ int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan);
+ if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) {
+ // Do not leave two adjacent spaces after deletion, or one at beginning of
+ // text
+ if (spanUnionEnd < editable.length() &&
+ Character.isSpaceChar(editable.charAt(spanUnionEnd)) &&
+ (spanUnionStart == 0 ||
+ Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) {
+ spanUnionEnd = spanUnionEnd + 1;
+ }
+ mTextView.deleteText_internal(spanUnionStart, spanUnionEnd);
+ }
+ hideWithCleanUp();
+ }
+ });
+
}
public boolean isShowingUp() {
@@ -2705,11 +2767,22 @@
mIsShowingUp = false;
}
- private class SuggestionInfo {
+ private final class SuggestionInfo {
int suggestionStart, suggestionEnd; // range of actual suggestion within text
- SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
+
+ // the SuggestionSpan that this TextView represents
+ @Nullable
+ SuggestionSpan suggestionSpan;
+
int suggestionIndex; // the index of this suggestion inside suggestionSpan
- SpannableStringBuilder text = new SpannableStringBuilder();
+
+ @Nullable
+ final SpannableStringBuilder text = new SpannableStringBuilder();
+
+ void clear() {
+ suggestionSpan = null;
+ text.clear();
+ }
}
private class SuggestionAdapter extends BaseAdapter {
@@ -2742,14 +2815,6 @@
final SuggestionInfo suggestionInfo = mSuggestionInfos[position];
textView.setText(suggestionInfo.text);
-
- if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY ||
- suggestionInfo.suggestionIndex == DELETE_TEXT) {
- textView.setBackgroundColor(Color.TRANSPARENT);
- } else {
- textView.setBackgroundColor(Color.WHITE);
- }
-
return textView;
}
}
@@ -2832,6 +2897,14 @@
width = Math.max(width, view.getMeasuredWidth());
}
+ if (mAddToDictionaryButton.getVisibility() != View.GONE) {
+ mAddToDictionaryButton.measure(horizontalMeasure, verticalMeasure);
+ width = Math.max(width, mAddToDictionaryButton.getMeasuredWidth());
+ }
+
+ mDeleteButton.measure(horizontalMeasure, verticalMeasure);
+ width = Math.max(width, mDeleteButton.getMeasuredWidth());
+
// Enforce the width based on actual text widths
mContentView.measure(
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
@@ -2863,9 +2936,12 @@
return Math.min(positionY, displayMetrics.heightPixels - height);
}
- @Override
- public void hide() {
- super.hide();
+ private void hideWithCleanUp() {
+ for (final SuggestionInfo info : mSuggestionInfos) {
+ info.clear();
+ }
+ mMisspelledSpan = null;
+ hide();
}
private boolean updateSuggestions() {
@@ -2880,7 +2956,7 @@
int spanUnionStart = mTextView.getText().length();
int spanUnionEnd = 0;
- SuggestionSpan misspelledSpan = null;
+ mMisspelledSpan = null;
int underlineColor = 0;
for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
@@ -2891,7 +2967,7 @@
spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
- misspelledSpan = suggestionSpan;
+ mMisspelledSpan = suggestionSpan;
}
// The first span dictates the background color of the highlighted text
@@ -2936,31 +3012,16 @@
highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
}
- // Add "Add to dictionary" item if there is a span with the misspelled flag
- if (misspelledSpan != null) {
- final int misspelledStart = spannable.getSpanStart(misspelledSpan);
- final int misspelledEnd = spannable.getSpanEnd(misspelledSpan);
+ // Make "Add to dictionary" item visible if there is a span with the misspelled flag
+ int addToDictionaryButtonVisibility = View.GONE;
+ if (mMisspelledSpan != null) {
+ final int misspelledStart = spannable.getSpanStart(mMisspelledSpan);
+ final int misspelledEnd = spannable.getSpanEnd(mMisspelledSpan);
if (misspelledStart >= 0 && misspelledEnd > misspelledStart) {
- SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
- suggestionInfo.suggestionSpan = misspelledSpan;
- suggestionInfo.suggestionIndex = ADD_TO_DICTIONARY;
- suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView.
- getContext().getString(com.android.internal.R.string.addToDictionary));
- suggestionInfo.text.setSpan(mHighlightSpan, 0, 0,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- mNumberOfSuggestions++;
+ addToDictionaryButtonVisibility = View.VISIBLE;
}
}
-
- // Delete item
- SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
- suggestionInfo.suggestionSpan = null;
- suggestionInfo.suggestionIndex = DELETE_TEXT;
- suggestionInfo.text.replace(0, suggestionInfo.text.length(),
- mTextView.getContext().getString(com.android.internal.R.string.deleteText));
- suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- mNumberOfSuggestions++;
+ mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility);
if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
if (underlineColor == 0) {
@@ -3004,103 +3065,70 @@
Editable editable = (Editable) mTextView.getText();
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
- if (suggestionInfo.suggestionIndex == DELETE_TEXT) {
- final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan);
- int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan);
- if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) {
- // Do not leave two adjacent spaces after deletion, or one at beginning of text
- if (spanUnionEnd < editable.length() &&
- Character.isSpaceChar(editable.charAt(spanUnionEnd)) &&
- (spanUnionStart == 0 ||
- Character.isSpaceChar(editable.charAt(spanUnionStart - 1)))) {
- spanUnionEnd = spanUnionEnd + 1;
- }
- mTextView.deleteText_internal(spanUnionStart, spanUnionEnd);
- }
- hide();
- return;
- }
-
final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan);
final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan);
if (spanStart < 0 || spanEnd <= spanStart) {
// Span has been removed
- hide();
+ hideWithCleanUp();
return;
}
final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
- if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY) {
- Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
- intent.putExtra("word", originalText);
- intent.putExtra("locale", mTextView.getTextServicesLocale().toString());
- // Put a listener to replace the original text with a word which the user
- // modified in a user dictionary dialog.
- intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- mTextView.getContext().startActivity(intent);
- // There is no way to know if the word was indeed added. Re-check.
- // TODO The ExtractEditText should remove the span in the original text instead
- editable.removeSpan(suggestionInfo.suggestionSpan);
- Selection.setSelection(editable, spanEnd);
- updateSpellCheckSpans(spanStart, spanEnd, false);
- } else {
- // SuggestionSpans are removed by replace: save them before
- SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
- SuggestionSpan.class);
- final int length = suggestionSpans.length;
- int[] suggestionSpansStarts = new int[length];
- int[] suggestionSpansEnds = new int[length];
- int[] suggestionSpansFlags = new int[length];
- for (int i = 0; i < length; i++) {
- final SuggestionSpan suggestionSpan = suggestionSpans[i];
- suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
- suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
- suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
+ // SuggestionSpans are removed by replace: save them before
+ final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
+ SuggestionSpan.class);
+ final int length = suggestionSpans.length;
+ final int[] suggestionSpansStarts = new int[length];
+ final int[] suggestionSpansEnds = new int[length];
+ final int[] suggestionSpansFlags = new int[length];
+ for (int i = 0; i < length; i++) {
+ final SuggestionSpan suggestionSpan = suggestionSpans[i];
+ suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
+ suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
+ suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
- // Remove potential misspelled flags
- int suggestionSpanFlags = suggestionSpan.getFlags();
- if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
- suggestionSpan.setFlags(suggestionSpanFlags);
- }
+ // Remove potential misspelled flags
+ int suggestionSpanFlags = suggestionSpan.getFlags();
+ if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
+ suggestionSpan.setFlags(suggestionSpanFlags);
}
-
- final int suggestionStart = suggestionInfo.suggestionStart;
- final int suggestionEnd = suggestionInfo.suggestionEnd;
- final String suggestion = suggestionInfo.text.subSequence(
- suggestionStart, suggestionEnd).toString();
- mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
-
- // Notify source IME of the suggestion pick. Do this before
- // swaping texts.
- suggestionInfo.suggestionSpan.notifySelection(
- mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
-
- // Swap text content between actual text and Suggestion span
- String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
- suggestions[suggestionInfo.suggestionIndex] = originalText;
-
- // Restore previous SuggestionSpans
- final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
- for (int i = 0; i < length; i++) {
- // Only spans that include the modified region make sense after replacement
- // Spans partially included in the replaced region are removed, there is no
- // way to assign them a valid range after replacement
- if (suggestionSpansStarts[i] <= spanStart &&
- suggestionSpansEnds[i] >= spanEnd) {
- mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
- suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
- }
- }
-
- // Move cursor at the end of the replaced word
- final int newCursorPosition = spanEnd + lengthDifference;
- mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}
- hide();
+ final int suggestionStart = suggestionInfo.suggestionStart;
+ final int suggestionEnd = suggestionInfo.suggestionEnd;
+ final String suggestion = suggestionInfo.text.subSequence(
+ suggestionStart, suggestionEnd).toString();
+ mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
+
+ // Notify source IME of the suggestion pick. Do this before
+ // swaping texts.
+ suggestionInfo.suggestionSpan.notifySelection(
+ mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
+
+ // Swap text content between actual text and Suggestion span
+ final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
+ suggestions[suggestionInfo.suggestionIndex] = originalText;
+
+ // Restore previous SuggestionSpans
+ final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
+ for (int i = 0; i < length; i++) {
+ // Only spans that include the modified region make sense after replacement
+ // Spans partially included in the replaced region are removed, there is no
+ // way to assign them a valid range after replacement
+ if (suggestionSpansStarts[i] <= spanStart &&
+ suggestionSpansEnds[i] >= spanEnd) {
+ mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
+ suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
+ }
+ }
+
+ // Move cursor at the end of the replaced word
+ final int newCursorPosition = spanEnd + lengthDifference;
+ mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
+ hideWithCleanUp();
}
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 280ff15..4d9f55c 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -21,12 +21,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -36,9 +32,6 @@
import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
-
/**
* FrameLayout is designed to block out an area on the screen to display
* a single item. Generally, FrameLayout should be used to hold a single child view, because it can
@@ -171,6 +164,10 @@
mPaddingBottom + mForegroundPaddingBottom;
}
+ @Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
+ }
/**
* {@inheritDoc}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index ad939be..ba868a1 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.view.ViewParent;
import com.android.internal.R;
import android.annotation.IntDef;
@@ -37,6 +38,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* A Layout that arranges its children in a single column or a single row. The direction of
@@ -644,6 +647,60 @@
}
}
+ @Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ // This implementation is almost exactly equivalent to the default implementation
+ // offered to the rest of the framework in ViewGroup, but we treat weight to be
+ // functionally equivalent to MATCH_PARENT along the orientation axis.
+
+ if (!checkPartialLayoutParams(child, LayoutParams.class)) return axisFilter;
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (child.didLayoutParamsChange()) {
+ // Anything could have changed about our previous assumptions.
+ return axisFilter;
+ }
+
+ // Our layout can always end up depending on a WRAP_CONTENT child.
+ final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
+ | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
+
+ if (wrapAxisFilter == axisFilter) {
+ // We know all queried axes are affected, just return early.
+ return wrapAxisFilter;
+ }
+
+ // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
+ // that our layout will remain stable within our parent. We need to ask.
+ int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
+ | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
+
+ // For LinearLayout, a nonzero weight is equivalent to MATCH_PARENT for this purpose.
+ if (lp.weight > 0) {
+ if (mOrientation == HORIZONTAL) {
+ matchAxisFilter |= FLAG_LAYOUT_AXIS_HORIZONTAL & axisFilter;
+ } else {
+ matchAxisFilter |= FLAG_LAYOUT_AXIS_VERTICAL & axisFilter;
+ }
+ }
+
+ if (matchAxisFilter != 0) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ // If our parent depends on us for an axis, then our layout can also be affected
+ // by a MATCH_PARENT child along that axis.
+ return getParent().findDependentLayoutAxes(this, matchAxisFilter)
+ | wrapAxisFilter;
+ }
+
+ // If we don't have a parent, assume we're affected
+ // in any determined affected direction.
+ return matchAxisFilter | wrapAxisFilter;
+ }
+
+ // Two exact sizes and LayoutParams didn't change. We're safe.
+ return 0;
+ }
+
/**
* Determines where to position dividers between children.
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 53ca6d1..b43ea76 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1158,7 +1158,7 @@
final View child = obtainView(0, mIsScrap);
// Lay out child directly against the parent measure spec so that
- // we can obtain exected minimum width and height.
+ // we can obtain expected minimum width and height.
measureScrapChild(child, 0, widthMeasureSpec, heightSize);
childWidth = child.getMeasuredWidth();
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 34a8439..027f874 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -19,9 +19,7 @@
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
-import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.ShowableListMenu;
-import com.android.internal.view.menu.SubMenuBuilder;
import android.annotation.MenuRes;
import android.content.Context;
@@ -33,35 +31,23 @@
import android.view.View.OnTouchListener;
/**
- * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
- * The popup will appear below the anchor view if there is room, or above it if there is not.
- * If the IME is visible the popup will not overlap it until it is touched. Touching outside
- * of the popup will dismiss it.
+ * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a
+ * {@link View}. The popup will appear below the anchor view if there is room,
+ * or above it if there is not. If the IME is visible the popup will not
+ * overlap it until it is touched. Touching outside of the popup will dismiss
+ * it.
*/
-public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
+public class PopupMenu {
private final Context mContext;
private final MenuBuilder mMenu;
private final View mAnchor;
private final MenuPopupHelper mPopup;
- private final boolean mShowCascadingMenus;
private OnMenuItemClickListener mMenuItemClickListener;
- private OnDismissListener mDismissListener;
+ private OnDismissListener mOnDismissListener;
private OnTouchListener mDragListener;
/**
- * Callback interface used to notify the application that the menu has closed.
- */
- public interface OnDismissListener {
- /**
- * Called when the associated menu has been dismissed.
- *
- * @param menu The PopupMenu that was dismissed.
- */
- public void onDismiss(PopupMenu menu);
- }
-
- /**
* Constructor to create a new popup menu with an anchor view.
*
* @param context Context the popup menu is running in, through which it
@@ -108,14 +94,33 @@
public PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr,
int popupStyleRes) {
mContext = context;
- mShowCascadingMenus = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
- mMenu = new MenuBuilder(context);
- mMenu.setCallback(this);
mAnchor = anchor;
+
+ mMenu = new MenuBuilder(context);
+ mMenu.setCallback(new MenuBuilder.Callback() {
+ @Override
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ if (mMenuItemClickListener != null) {
+ return mMenuItemClickListener.onMenuItemClick(item);
+ }
+ return false;
+ }
+
+ @Override
+ public void onMenuModeChange(MenuBuilder menu) {
+ }
+ });
+
mPopup = new MenuPopupHelper(context, mMenu, anchor, false, popupStyleAttr, popupStyleRes);
mPopup.setGravity(gravity);
- mPopup.setCallback(this);
+ mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
+ @Override
+ public void onDismiss() {
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss(PopupMenu.this);
+ }
+ }
+ });
}
/**
@@ -125,7 +130,6 @@
* the next time the popup is shown.
*
* @param gravity the gravity used to align the popup window
- *
* @see #getGravity()
*/
public void setGravity(int gravity) {
@@ -134,7 +138,6 @@
/**
* @return the gravity used to align the popup window to its anchor view
- *
* @see #setGravity(int)
*/
public int getGravity() {
@@ -146,8 +149,8 @@
* to implement drag-to-open behavior.
* <p>
* When the listener is set on a view, touching that view and dragging
- * outside of its bounds will open the popup window. Lifting will select the
- * currently touched list item.
+ * outside of its bounds will open the popup window. Lifting will select
+ * the currently touched list item.
* <p>
* Example usage:
* <pre>
@@ -184,9 +187,10 @@
}
/**
- * @return the {@link Menu} associated with this popup. Populate the returned Menu with
- * items before calling {@link #show()}.
+ * Returns the {@link Menu} associated with this popup. Populate the
+ * returned Menu with items before calling {@link #show()}.
*
+ * @return the {@link Menu} associated with this popup
* @see #show()
* @see #getMenuInflater()
*/
@@ -195,9 +199,8 @@
}
/**
- * @return a {@link MenuInflater} that can be used to inflate menu items from XML into the
- * menu returned by {@link #getMenu()}.
- *
+ * @return a {@link MenuInflater} that can be used to inflate menu items
+ * from XML into the menu returned by {@link #getMenu()}
* @see #getMenu()
*/
public MenuInflater getMenuInflater() {
@@ -205,8 +208,9 @@
}
/**
- * Inflate a menu resource into this PopupMenu. This is equivalent to calling
- * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
+ * Inflate a menu resource into this PopupMenu. This is equivalent to
+ * calling {@code popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu())}.
+ *
* @param menuRes Menu resource to inflate
*/
public void inflate(@MenuRes int menuRes) {
@@ -215,6 +219,7 @@
/**
* Show the menu popup anchored to the view specified during construction.
+ *
* @see #dismiss()
*/
public void show() {
@@ -223,6 +228,7 @@
/**
* Dismiss the menu popup.
+ *
* @see #show()
*/
public void dismiss() {
@@ -230,68 +236,49 @@
}
/**
- * Set a listener that will be notified when the user selects an item from the menu.
+ * Sets a listener that will be notified when the user selects an item from
+ * the menu.
*
- * @param listener Listener to notify
+ * @param listener the listener to notify
*/
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mMenuItemClickListener = listener;
}
/**
- * Set a listener that will be notified when this menu is dismissed.
+ * Sets a listener that will be notified when this menu is dismissed.
*
- * @param listener Listener to notify
+ * @param listener the listener to notify
*/
public void setOnDismissListener(OnDismissListener listener) {
- mDismissListener = listener;
+ mOnDismissListener = listener;
}
/**
- * @hide
- */
- public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- if (mMenuItemClickListener != null) {
- return mMenuItemClickListener.onMenuItemClick(item);
- }
- return false;
- }
-
- /**
- * @hide
- */
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- if (mDismissListener != null) {
- mDismissListener.onDismiss(this);
- }
- }
-
- /**
- * @hide
- */
- public boolean onOpenSubMenu(MenuBuilder subMenu) {
- // The menu presenter will handle opening the submenu itself. Nothing to do here.
- return false;
- }
-
- /**
- * @hide
- */
- public void onMenuModeChange(MenuBuilder menu) {
- }
-
- /**
- * Interface responsible for receiving menu item click events if the items themselves
- * do not have individual item click listeners.
+ * Interface responsible for receiving menu item click events if the items
+ * themselves do not have individual item click listeners.
*/
public interface OnMenuItemClickListener {
/**
- * This method will be invoked when a menu item is clicked if the item itself did
- * not already handle the event.
+ * This method will be invoked when a menu item is clicked if the item
+ * itself did not already handle the event.
*
- * @param item {@link MenuItem} that was clicked
- * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
+ * @param item the menu item that was clicked
+ * @return {@code true} if the event was handled, {@code false}
+ * otherwise
*/
- public boolean onMenuItemClick(MenuItem item);
+ boolean onMenuItemClick(MenuItem item);
+ }
+
+ /**
+ * Callback interface used to notify the application that the menu has closed.
+ */
+ public interface OnDismissListener {
+ /**
+ * Called when the associated menu has been dismissed.
+ *
+ * @param menu the popup menu that was dismissed
+ */
+ void onDismiss(PopupMenu menu);
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index ca1b211..ce1c108 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
import android.app.ActivityThread;
import android.app.Application;
import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.ContextWrapper;
@@ -153,6 +154,13 @@
};
/**
+ * @hide
+ */
+ public void setRemoteInputs(int viewId, RemoteInput[] remoteInputs) {
+ mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
+ }
+
+ /**
* Handle with care!
*/
static class MutablePair<F, S> {
@@ -1699,6 +1707,43 @@
}
/**
+ * Helper action to add a view tag with RemoteInputs.
+ */
+ private class SetRemoteInputsAction extends Action {
+
+ public SetRemoteInputsAction(int viewId, RemoteInput[] remoteInputs) {
+ this.viewId = viewId;
+ this.remoteInputs = remoteInputs;
+ }
+
+ public SetRemoteInputsAction(Parcel parcel) {
+ viewId = parcel.readInt();
+ remoteInputs = parcel.readParcelableArray(RemoteInput.class.getClassLoader());
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(TAG);
+ dest.writeInt(viewId);
+ dest.writeParcelableArray(remoteInputs, flags);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ final TextView target = (TextView) root.findViewById(viewId);
+ if (target == null) return;
+
+ target.setTagInternal(R.id.remote_input_tag, remoteInputs);
+ }
+
+ public String getActionName() {
+ return "SetRemoteInputsAction";
+ }
+
+ final Parcelable[] remoteInputs;
+ public final static int TAG = 18;
+ }
+
+ /**
* Simple class used to keep track of memory usage in a RemoteViews.
*
*/
@@ -1894,6 +1939,9 @@
case TextViewDrawableColorFilterAction.TAG:
mActions.add(new TextViewDrawableColorFilterAction(parcel));
break;
+ case SetRemoteInputsAction.TAG:
+ mActions.add(new SetRemoteInputsAction(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index e9325ef..6edce91 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -34,8 +35,10 @@
import android.util.IntArray;
import android.util.MathUtils;
import android.util.StateSet;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -61,11 +64,14 @@
private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
+ private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0;
+
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
private final Paint mDaySelectorPaint = new Paint();
private final Paint mDayHighlightPaint = new Paint();
+ private final Paint mDayHighlightSelectorPaint = new Paint();
private final Calendar mCalendar = Calendar.getInstance();
private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
@@ -130,7 +136,9 @@
private ColorStateList mDayTextColor;
- private int mTouchedItem = -1;
+ private int mHighlightedDay = -1;
+ private int mPreviouslyHighlightedDay = -1;
+ private boolean mIsTouchHighlighted = false;
public SimpleMonthView(Context context) {
this(context, null);
@@ -268,6 +276,9 @@
mDayHighlightPaint.setAntiAlias(true);
mDayHighlightPaint.setStyle(Style.FILL);
+ mDayHighlightSelectorPaint.setAntiAlias(true);
+ mDayHighlightSelectorPaint.setStyle(Style.FILL);
+
mDayPaint.setAntiAlias(true);
mDayPaint.setTextSize(dayTextSize);
mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
@@ -296,6 +307,8 @@
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
mDaySelectorPaint.setColor(activatedColor);
+ mDayHighlightSelectorPaint.setColor(activatedColor);
+ mDayHighlightSelectorPaint.setAlpha(SELECTED_HIGHLIGHT_ALPHA);
invalidate();
}
@@ -326,8 +339,10 @@
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int touchedItem = getDayAtLocation(x, y);
- if (mTouchedItem != touchedItem) {
- mTouchedItem = touchedItem;
+ mIsTouchHighlighted = true;
+ if (mHighlightedDay != touchedItem) {
+ mHighlightedDay = touchedItem;
+ mPreviouslyHighlightedDay = touchedItem;
invalidate();
}
if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
@@ -342,7 +357,8 @@
// Fall through.
case MotionEvent.ACTION_CANCEL:
// Reset touched day on stream end.
- mTouchedItem = -1;
+ mHighlightedDay = -1;
+ mIsTouchHighlighted = false;
invalidate();
break;
}
@@ -350,6 +366,228 @@
}
@Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // We need to handle focus change within the SimpleMonthView because we are simulating
+ // multiple Views. The arrow keys will move between days until there is no space (no
+ // day to the left, top, right, or bottom). Focus forward and back jumps out of the
+ // SimpleMonthView, skipping over other SimpleMonthViews in the parent ViewPager
+ // to the next focusable View in the hierarchy.
+ boolean focusChanged = false;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (event.hasNoModifiers()) {
+ focusChanged = moveOneDay(isLayoutRtl());
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (event.hasNoModifiers()) {
+ focusChanged = moveOneDay(!isLayoutRtl());
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.hasNoModifiers()) {
+ ensureFocusedDay();
+ if (mHighlightedDay > 7) {
+ mHighlightedDay -= 7;
+ focusChanged = true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.hasNoModifiers()) {
+ ensureFocusedDay();
+ if (mHighlightedDay <= mDaysInMonth - 7) {
+ mHighlightedDay += 7;
+ focusChanged = true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ if (mHighlightedDay != -1) {
+ onDayClicked(mHighlightedDay);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB: {
+ int focusChangeDirection = 0;
+ if (event.hasNoModifiers()) {
+ focusChangeDirection = View.FOCUS_FORWARD;
+ } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+ focusChangeDirection = View.FOCUS_BACKWARD;
+ }
+ if (focusChangeDirection != 0) {
+ final ViewParent parent = getParent();
+ // move out of the ViewPager next/previous
+ View nextFocus = this;
+ do {
+ nextFocus = nextFocus.focusSearch(focusChangeDirection);
+ } while (nextFocus != null && nextFocus != this &&
+ nextFocus.getParent() == parent);
+ if (nextFocus != null) {
+ nextFocus.requestFocus();
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ if (focusChanged) {
+ invalidate();
+ return true;
+ } else {
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+
+ private boolean moveOneDay(boolean positive) {
+ ensureFocusedDay();
+ boolean focusChanged = false;
+ if (positive) {
+ if (!isLastDayOfWeek(mHighlightedDay) && mHighlightedDay < mDaysInMonth) {
+ mHighlightedDay++;
+ focusChanged = true;
+ }
+ } else {
+ if (!isFirstDayOfWeek(mHighlightedDay) && mHighlightedDay > 1) {
+ mHighlightedDay--;
+ focusChanged = true;
+ }
+ }
+ return focusChanged;
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
+ @Nullable Rect previouslyFocusedRect) {
+ if (gainFocus) {
+ // If we've gained focus through arrow keys, we should find the day closest
+ // to the focus rect. If we've gained focus through forward/back, we should
+ // focus on the selected day if there is one.
+ final int offset = findDayOffset();
+ switch(direction) {
+ case View.FOCUS_RIGHT: {
+ int row = findClosestRow(previouslyFocusedRect);
+ mHighlightedDay = row == 0 ? 1 : (row * DAYS_IN_WEEK) - offset + 1;
+ break;
+ }
+ case View.FOCUS_LEFT: {
+ int row = findClosestRow(previouslyFocusedRect) + 1;
+ mHighlightedDay = Math.min(mDaysInMonth, (row * DAYS_IN_WEEK) - offset);
+ break;
+ }
+ case View.FOCUS_DOWN: {
+ final int col = findClosestColumn(previouslyFocusedRect);
+ final int day = col - offset + 1;
+ mHighlightedDay = day < 1 ? day + DAYS_IN_WEEK : day;
+ break;
+ }
+ case View.FOCUS_UP: {
+ final int col = findClosestColumn(previouslyFocusedRect);
+ final int maxWeeks = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int day = col - offset + (DAYS_IN_WEEK * maxWeeks) + 1;
+ mHighlightedDay = day > mDaysInMonth ? day - DAYS_IN_WEEK : day;
+ break;
+ }
+ }
+ ensureFocusedDay();
+ invalidate();
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+
+ /**
+ * Returns the row (0 indexed) closest to previouslyFocusedRect or center if null.
+ */
+ private int findClosestRow(@Nullable Rect previouslyFocusedRect) {
+ if (previouslyFocusedRect == null) {
+ return 3;
+ } else {
+ int centerY = previouslyFocusedRect.centerY();
+
+ final TextPaint p = mDayPaint;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int rowHeight = mDayHeight;
+
+ // Text is vertically centered within the row height.
+ final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+ final int rowCenter = headerHeight + rowHeight / 2;
+
+ centerY -= rowCenter - halfLineHeight;
+ int row = Math.round(centerY / (float) rowHeight);
+ final int maxDay = findDayOffset() + mDaysInMonth;
+ final int maxRows = (maxDay / DAYS_IN_WEEK) - ((maxDay % DAYS_IN_WEEK == 0) ? 1 : 0);
+
+ row = MathUtils.constrain(row, 0, maxRows);
+ return row;
+ }
+ }
+
+ /**
+ * Returns the column (0 indexed) closest to the previouslyFocusedRect or center if null.
+ * The 0 index is related to the first day of the week.
+ */
+ private int findClosestColumn(@Nullable Rect previouslyFocusedRect) {
+ if (previouslyFocusedRect == null) {
+ return DAYS_IN_WEEK / 2;
+ } else {
+ int centerX = previouslyFocusedRect.centerX() - mPaddingLeft;
+ final int columnFromLeft =
+ MathUtils.constrain(centerX / mCellWidth, 0, DAYS_IN_WEEK - 1);
+ return isLayoutRtl() ? DAYS_IN_WEEK - columnFromLeft - 1: columnFromLeft;
+ }
+ }
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ if (mHighlightedDay > 0) {
+ getBoundsForDay(mHighlightedDay, r);
+ } else {
+ super.getFocusedRect(r);
+ }
+ }
+
+ @Override
+ protected void onFocusLost() {
+ if (!mIsTouchHighlighted) {
+ // Unhighlight a day.
+ mPreviouslyHighlightedDay = mHighlightedDay;
+ mHighlightedDay = -1;
+ invalidate();
+ }
+ super.onFocusLost();
+ }
+
+ /**
+ * Ensure some day is highlighted. If a day isn't highlighted, it chooses the selected day,
+ * if possible, or the first day of the month if not.
+ */
+ private void ensureFocusedDay() {
+ if (mHighlightedDay != -1) {
+ return;
+ }
+ if (mPreviouslyHighlightedDay != -1) {
+ mHighlightedDay = mPreviouslyHighlightedDay;
+ return;
+ }
+ if (mActivatedDay != -1) {
+ mHighlightedDay = mActivatedDay;
+ return;
+ }
+ mHighlightedDay = 1;
+ }
+
+ private boolean isFirstDayOfWeek(int day) {
+ final int offset = findDayOffset();
+ return (offset + day - 1) % DAYS_IN_WEEK == 0;
+ }
+
+ private boolean isLastDayOfWeek(int day) {
+ final int offset = findDayOffset();
+ return (offset + day) % DAYS_IN_WEEK == 0;
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
@@ -432,12 +670,15 @@
}
final boolean isDayActivated = mActivatedDay == day;
+ final boolean isDayHighlighted = mHighlightedDay == day;
if (isDayActivated) {
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
- } else if (mTouchedItem == day) {
+ final Paint paint = isDayHighlighted ? mDayHighlightSelectorPaint :
+ mDaySelectorPaint;
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, paint);
+ } else if (isDayHighlighted) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
if (isDayEnabled) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index c54a574..476c6a2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -624,7 +624,7 @@
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
int mCursorDrawableRes;
- // These four fields, could be moved to Editor, since we know their default values and we
+ // These six fields, could be moved to Editor, since we know their default values and we
// could condition the creation of the Editor to a non standard value. This is however
// brittle since the hardcoded values here (such as
// com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the
@@ -633,6 +633,8 @@
int mTextSelectHandleRightRes;
int mTextSelectHandleRes;
int mTextEditSuggestionItemLayout;
+ int mTextEditSuggestionContainerLayout;
+ int mTextEditSuggestionHighlightStyle;
/**
* EditText specific data, created on demand when one of the Editor fields is used.
@@ -1155,6 +1157,14 @@
mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
break;
+ case com.android.internal.R.styleable.TextView_textEditSuggestionContainerLayout:
+ mTextEditSuggestionContainerLayout = a.getResourceId(attr, 0);
+ break;
+
+ case com.android.internal.R.styleable.TextView_textEditSuggestionHighlightStyle:
+ mTextEditSuggestionHighlightStyle = a.getResourceId(attr, 0);
+ break;
+
case com.android.internal.R.styleable.TextView_textIsSelectable:
setTextIsSelectable(a.getBoolean(attr, false));
break;
@@ -3366,10 +3376,17 @@
}
/**
- * Sets whether the movement method will automatically be set to
- * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
- * set to nonzero and links are detected in {@link #setText}.
- * The default is true.
+ * Sets whether the movement method will automatically be set to {@link LinkMovementMethod}
+ * after {@link #setText} or {@link #append} is called. The movement method is set if one of the
+ * following is true:
+ * <ul>
+ * <li>{@link #setAutoLinkMask} has been set to nonzero and links are detected in
+ * {@link #setText} or {@link #append}.
+ * <li>The input for {@link #setText} or {@link #append} contains a {@link ClickableSpan}.
+ * </ul>
+ *
+ * <p>This function does not have an immediate effect, movement method will be set only after a
+ * call to {@link #setText} or {@link #append}. The default is true.</p>
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
@@ -3379,10 +3396,14 @@
}
/**
- * Returns whether the movement method will automatically be set to
- * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
- * set to nonzero and links are detected in {@link #setText}.
- * The default is true.
+ * Returns whether the movement method will automatically be set to {@link LinkMovementMethod}
+ * after {@link #setText} or {@link #append} is called.
+ *
+ * See {@link #setLinksClickable} for details.
+ *
+ * <p>The default is true.</p>
+ *
+ * @see #setLinksClickable
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
@@ -3975,6 +3996,21 @@
}
((Editable) mText).append(text, start, end);
+
+ boolean hasClickableSpans = false;
+ if (mAutoLinkMask != 0) {
+ hasClickableSpans = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+ } else if (mLinksClickable && text instanceof Spanned) {
+ ClickableSpan[] clickableSpans =
+ ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+ hasClickableSpans = clickableSpans != null && clickableSpans.length > 0;
+ }
+
+ // Do not change the movement method for text that supports text selection as it
+ // would prevent an arbitrary cursor displacement.
+ if (hasClickableSpans && mLinksClickable && !textCanBeSelected()) {
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
}
private void updateTextColors() {
@@ -4318,6 +4354,7 @@
text = TextUtils.stringOrSpannedString(text);
}
+ boolean hasClickableSpans = false;
if (mAutoLinkMask != 0) {
Spannable s2;
@@ -4327,22 +4364,32 @@
s2 = mSpannableFactory.newSpannable(text);
}
- if (Linkify.addLinks(s2, mAutoLinkMask)) {
+ hasClickableSpans = Linkify.addLinks(s2, mAutoLinkMask);
+ if (hasClickableSpans) {
text = s2;
- type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
+ }
+ } else if (mLinksClickable && text instanceof Spanned) {
+ ClickableSpan[] clickableSpans =
+ ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+ hasClickableSpans = clickableSpans != null && clickableSpans.length > 0;
+ if (hasClickableSpans && !(text instanceof Spannable)) {
+ text = mSpannableFactory.newSpannable(text);
+ }
+ }
- /*
- * We must go ahead and set the text before changing the
- * movement method, because setMovementMethod() may call
- * setText() again to try to upgrade the buffer type.
- */
- mText = text;
+ if (hasClickableSpans) {
+ type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
+ /*
+ * We must go ahead and set the text before changing the
+ * movement method, because setMovementMethod() may call
+ * setText() again to try to upgrade the buffer type.
+ */
+ mText = text;
- // Do not change the movement method for text that support text selection as it
- // would prevent an arbitrary cursor displacement.
- if (mLinksClickable && !textCanBeSelected()) {
- setMovementMethod(LinkMovementMethod.getInstance());
- }
+ // Do not change the movement method for text that supports text selection as it
+ // would prevent an arbitrary cursor displacement.
+ if (mLinksClickable && !textCanBeSelected()) {
+ setMovementMethod(LinkMovementMethod.getInstance());
}
}
@@ -6764,10 +6811,11 @@
if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
if (!compressText(ellipsisWidth)) {
- final int height = mLayoutParams.height;
// If the size of the view does not depend on the size of the text, try to
// start the marquee immediately
- if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.MATCH_PARENT) {
+ final ViewParent parent = getParent();
+ if (parent != null && parent.findDependentLayoutAxes(this,
+ ViewParent.FLAG_LAYOUT_AXIS_VERTICAL) == 0) {
startMarquee();
} else {
// Defer the start of the marquee until we know our width (see setFrame())
@@ -7163,37 +7211,9 @@
* new view layout.
*/
private void checkForResize() {
- boolean sizeChanged = false;
-
- if (mLayout != null) {
- // Check if our width changed
- if (mLayoutParams.width == LayoutParams.WRAP_CONTENT) {
- sizeChanged = true;
- invalidate();
- }
-
- // Check if our height changed
- if (mLayoutParams.height == LayoutParams.WRAP_CONTENT) {
- int desiredHeight = getDesiredHeight();
-
- if (desiredHeight != this.getHeight()) {
- sizeChanged = true;
- }
- } else if (mLayoutParams.height == LayoutParams.MATCH_PARENT) {
- if (mDesiredHeightAtMeasure >= 0) {
- int desiredHeight = getDesiredHeight();
-
- if (desiredHeight != mDesiredHeightAtMeasure) {
- sizeChanged = true;
- }
- }
- }
- }
-
- if (sizeChanged) {
- requestLayout();
- // caller will have already invalidated
- }
+ // Always request a layout. The parent will perform the correct version
+ // of the intended optimizations as part of requestLayoutForChild.
+ requestLayout();
}
/**
@@ -7201,56 +7221,10 @@
* or merely a new text layout.
*/
private void checkForRelayout() {
- // If we have a fixed width, we can just swap in a new text layout
- // if the text height stays the same or if the view height is fixed.
-
- if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT ||
- (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) &&
- (mHint == null || mHintLayout != null) &&
- (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
- // Static width, so try making a new text layout.
-
- int oldht = mLayout.getHeight();
- int want = mLayout.getWidth();
- int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
-
- /*
- * No need to bring the text into view, since the size is not
- * changing (unless we do the requestLayout(), in which case it
- * will happen at measure).
- */
- makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
- mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
- false);
-
- if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
- // In a fixed-height view, so use our new text layout.
- if (mLayoutParams.height != LayoutParams.WRAP_CONTENT &&
- mLayoutParams.height != LayoutParams.MATCH_PARENT) {
- invalidate();
- return;
- }
-
- // Dynamic height, but height has stayed the same,
- // so use our new text layout.
- if (mLayout.getHeight() == oldht &&
- (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
- invalidate();
- return;
- }
- }
-
- // We lose: the height has changed and we have a dynamic height.
- // Request a new view layout using our new text layout.
- requestLayout();
- invalidate();
- } else {
- // Dynamic width, so we have no choice but to request a new
- // view layout with a new text layout.
- nullLayouts();
- requestLayout();
- invalidate();
- }
+ // Always request a layout. The parent will perform the correct version
+ // of the intended optimizations as part of requestLayoutForChild.
+ nullLayouts();
+ requestLayout();
}
@Override
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 8e5af79..a24d37f 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -100,7 +100,7 @@
* @see #getHour()
*/
public void setHour(int hour) {
- mDelegate.setCurrentHour(hour);
+ mDelegate.setHour(hour);
}
/**
@@ -110,7 +110,7 @@
* @see #setHour(int)
*/
public int getHour() {
- return mDelegate.getCurrentHour();
+ return mDelegate.getHour();
}
/**
@@ -120,7 +120,7 @@
* @see #getMinute()
*/
public void setMinute(int minute) {
- mDelegate.setCurrentMinute(minute);
+ mDelegate.setMinute(minute);
}
/**
@@ -130,7 +130,7 @@
* @see #setMinute(int)
*/
public int getMinute() {
- return mDelegate.getCurrentMinute();
+ return mDelegate.getMinute();
}
/**
@@ -150,7 +150,7 @@
@NonNull
@Deprecated
public Integer getCurrentHour() {
- return mDelegate.getCurrentHour();
+ return mDelegate.getHour();
}
/**
@@ -160,7 +160,7 @@
*/
@Deprecated
public void setCurrentMinute(@NonNull Integer currentMinute) {
- mDelegate.setCurrentMinute(currentMinute);
+ mDelegate.setMinute(currentMinute);
}
/**
@@ -170,7 +170,7 @@
@NonNull
@Deprecated
public Integer getCurrentMinute() {
- return mDelegate.getCurrentMinute();
+ return mDelegate.getMinute();
}
/**
@@ -186,7 +186,7 @@
return;
}
- mDelegate.setIs24HourView(is24HourView);
+ mDelegate.setIs24Hour(is24HourView);
}
/**
@@ -195,7 +195,7 @@
* @see #setIs24HourView(Boolean)
*/
public boolean is24HourView() {
- return mDelegate.is24HourView();
+ return mDelegate.is24Hour();
}
/**
@@ -207,16 +207,6 @@
mDelegate.setOnTimeChangedListener(onTimeChangedListener);
}
- /**
- * Sets the callback that indicates the current time is valid.
- *
- * @param callback the callback, may be null
- * @hide
- */
- public void setValidationCallback(@Nullable ValidationCallback callback) {
- mDelegate.setValidationCallback(callback);
- }
-
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -234,12 +224,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mDelegate.onConfigurationChanged(newConfig);
- }
-
- @Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return mDelegate.onSaveInstanceState(superState);
@@ -269,25 +253,22 @@
* for the real behavior.
*/
interface TimePickerDelegate {
- void setCurrentHour(int currentHour);
- int getCurrentHour();
+ void setHour(int hour);
+ int getHour();
- void setCurrentMinute(int currentMinute);
- int getCurrentMinute();
+ void setMinute(int minute);
+ int getMinute();
- void setIs24HourView(boolean is24HourView);
- boolean is24HourView();
+ void setIs24Hour(boolean is24Hour);
+ boolean is24Hour();
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
- void setValidationCallback(ValidationCallback callback);
void setEnabled(boolean enabled);
boolean isEnabled();
int getBaseline();
- void onConfigurationChanged(Configuration newConfig);
-
Parcelable onSaveInstanceState(Parcelable superState);
void onRestoreInstanceState(Parcelable state);
@@ -295,16 +276,6 @@
void onPopulateAccessibilityEvent(AccessibilityEvent event);
}
- /**
- * A callback interface for updating input validity when the TimePicker
- * when included into a Dialog.
- *
- * @hide
- */
- public static interface ValidationCallback {
- void onValidationChanged(boolean valid);
- }
-
static String[] getAmPmStrings(Context context) {
final Locale locale = context.getResources().getConfiguration().locale;
final LocaleData d = LocaleData.get(locale);
@@ -319,43 +290,16 @@
* An abstract class which can be used as a start for TimePicker implementations
*/
abstract static class AbstractTimePickerDelegate implements TimePickerDelegate {
- // The delegator
- protected TimePicker mDelegator;
+ protected final TimePicker mDelegator;
+ protected final Context mContext;
+ protected final Locale mLocale;
- // The context
- protected Context mContext;
-
- // The current locale
- protected Locale mCurrentLocale;
-
- // Callbacks
protected OnTimeChangedListener mOnTimeChangedListener;
- protected ValidationCallback mValidationCallback;
- public AbstractTimePickerDelegate(TimePicker delegator, Context context) {
+ public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) {
mDelegator = delegator;
mContext = context;
-
- // initialization based on locale
- setCurrentLocale(Locale.getDefault());
- }
-
- public void setCurrentLocale(Locale locale) {
- if (locale.equals(mCurrentLocale)) {
- return;
- }
- mCurrentLocale = locale;
- }
-
- @Override
- public void setValidationCallback(ValidationCallback callback) {
- mValidationCallback = callback;
- }
-
- protected void onValidationChanged(boolean valid) {
- if (mValidationCallback != null) {
- mValidationCallback.onValidationChanged(valid);
- }
+ mLocale = context.getResources().getConfiguration().locale;
}
}
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 4dc5fd3e..1d6e52c 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Parcel;
@@ -85,11 +84,13 @@
private final RadialTimePickerView mRadialTimePickerView;
private final TextView mSeparatorView;
+ private final Calendar mTempCalendar;
+
private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
private int mInitialMinute;
- private boolean mIs24HourView;
+ private boolean mIs24Hour;
private boolean mIsAmPmAtStart;
// Accessibility strings.
@@ -104,8 +105,6 @@
private CharSequence mLastAnnouncedText;
private boolean mLastAnnouncedIsHour;
- private Calendar mTempCalendar;
-
public TimePickerClockDelegate(TimePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
@@ -199,20 +198,13 @@
mAllowAutoAdvance = true;
- // Updates mHourFormat variables used below.
- updateHourFormat(mCurrentLocale, mIs24HourView);
-
- // Update hour text field.
- final int minHour = mHourFormatStartsAtZero ? 0 : 1;
- final int maxHour = (mIs24HourView ? 23 : 11) + minHour;
- mHourView.setRange(minHour, maxHour);
- mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
+ updateHourFormat();
// Initialize with current time.
- mTempCalendar = Calendar.getInstance(mCurrentLocale);
+ mTempCalendar = Calendar.getInstance(mLocale);
final int currentHour = mTempCalendar.get(Calendar.HOUR_OF_DAY);
final int currentMinute = mTempCalendar.get(Calendar.MINUTE);
- initialize(currentHour, currentMinute, mIs24HourView, HOUR_INDEX);
+ initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX);
}
/**
@@ -233,15 +225,14 @@
}
/**
- * Determines how the hour should be formatted and updates member variables
- * related to hour formatting.
- *
- * @param locale the locale in which the view is displayed
- * @param is24Hour whether the view is in 24-hour (hour-of-day) mode
+ * Updates hour formatting based on the current locale and 24-hour mode.
+ * <p>
+ * Determines how the hour should be formatted, sets member variables for
+ * leading zero and starting hour, and sets the hour view's presentation.
*/
- private void updateHourFormat(Locale locale, boolean is24Hour) {
+ private void updateHourFormat() {
final String bestDateTimePattern = DateFormat.getBestDateTimePattern(
- locale, is24Hour ? "Hm" : "hm");
+ mLocale, mIs24Hour ? "Hm" : "hm");
final int lengthPattern = bestDateTimePattern.length();
boolean showLeadingZero = false;
char hourFormat = '\0';
@@ -259,6 +250,12 @@
mHourFormatShowLeadingZero = showLeadingZero;
mHourFormatStartsAtZero = hourFormat == 'K' || hourFormat == 'H';
+
+ // Update hour text field.
+ final int minHour = mHourFormatStartsAtZero ? 0 : 1;
+ final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
+ mHourView.setRange(minHour, maxHour);
+ mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
}
private static final CharSequence obtainVerbatim(String text) {
@@ -333,7 +330,7 @@
private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
mInitialHourOfDay = hourOfDay;
mInitialMinute = minute;
- mIs24HourView = is24HourView;
+ mIs24Hour = is24HourView;
updateUI(index);
}
@@ -352,17 +349,16 @@
}
private void updateRadialPicker(int index) {
- mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView);
+ mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24Hour);
setCurrentItemShowing(index, false, true);
}
private void updateHeaderAmPm() {
-
- if (mIs24HourView) {
+ if (mIs24Hour) {
mAmPmLayout.setVisibility(View.GONE);
} else {
// Ensure that AM/PM layout is in the correct position.
- final String dateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, "hm");
+ final String dateTimePattern = DateFormat.getBestDateTimePattern(mLocale, "hm");
final boolean isAmPmAtStart = dateTimePattern.startsWith("a");
setAmPmAtStart(isAmPmAtStart);
@@ -395,35 +391,32 @@
* Set the current hour.
*/
@Override
- public void setCurrentHour(int currentHour) {
- if (mInitialHourOfDay == currentHour) {
- return;
+ public void setHour(int hour) {
+ if (mInitialHourOfDay != hour) {
+ mInitialHourOfDay = hour;
+ updateHeaderHour(hour, true);
+ updateHeaderAmPm();
+ mRadialTimePickerView.setCurrentHour(hour);
+ mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
+ mDelegator.invalidate();
+ onTimeChanged();
}
- mInitialHourOfDay = currentHour;
- updateHeaderHour(currentHour, true);
- updateHeaderAmPm();
- mRadialTimePickerView.setCurrentHour(currentHour);
- mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
- mDelegator.invalidate();
- onTimeChanged();
}
/**
- * @return The current hour in the range (0-23).
+ * @return the current hour in the range (0-23)
*/
@Override
- public int getCurrentHour() {
- int currentHour = mRadialTimePickerView.getCurrentHour();
- if (mIs24HourView) {
+ public int getHour() {
+ final int currentHour = mRadialTimePickerView.getCurrentHour();
+ if (mIs24Hour) {
return currentHour;
+ }
+
+ if (mRadialTimePickerView.getAmOrPm() == PM) {
+ return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
} else {
- switch(mRadialTimePickerView.getAmOrPm()) {
- case PM:
- return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
- case AM:
- default:
- return currentHour % HOURS_IN_HALF_DAY;
- }
+ return currentHour % HOURS_IN_HALF_DAY;
}
}
@@ -431,48 +424,49 @@
* Set the current minute (0-59).
*/
@Override
- public void setCurrentMinute(int currentMinute) {
- if (mInitialMinute == currentMinute) {
- return;
+ public void setMinute(int minute) {
+ if (mInitialMinute != minute) {
+ mInitialMinute = minute;
+ updateHeaderMinute(minute, true);
+ mRadialTimePickerView.setCurrentMinute(minute);
+ mDelegator.invalidate();
+ onTimeChanged();
}
- mInitialMinute = currentMinute;
- updateHeaderMinute(currentMinute, true);
- mRadialTimePickerView.setCurrentMinute(currentMinute);
- mDelegator.invalidate();
- onTimeChanged();
}
/**
* @return The current minute.
*/
@Override
- public int getCurrentMinute() {
+ public int getMinute() {
return mRadialTimePickerView.getCurrentMinute();
}
/**
- * Set whether in 24 hour or AM/PM mode.
+ * Sets whether time is displayed in 24-hour mode or 12-hour mode with
+ * AM/PM indicators.
*
- * @param is24HourView True = 24 hour mode. False = AM/PM.
+ * @param is24Hour {@code true} to display time in 24-hour mode or
+ * {@code false} for 12-hour mode with AM/PM
*/
- @Override
- public void setIs24HourView(boolean is24HourView) {
- if (is24HourView == mIs24HourView) {
- return;
+ public void setIs24Hour(boolean is24Hour) {
+ if (mIs24Hour != is24Hour) {
+ mIs24Hour = is24Hour;
+ mInitialHourOfDay = getHour();
+
+ updateHourFormat();
+ updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
-
- mIs24HourView = is24HourView;
- mInitialHourOfDay = getCurrentHour();
-
- updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
/**
- * @return true if this is in 24 hour view else false.
+ * @return {@code true} if time is displayed in 24-hour mode, or
+ * {@code false} if time is displayed in 12-hour mode with AM/PM
+ * indicators
*/
@Override
- public boolean is24HourView() {
- return mIs24HourView;
+ public boolean is24Hour() {
+ return mIs24Hour;
}
@Override
@@ -502,14 +496,9 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- updateUI(mRadialTimePickerView.getCurrentItemShowing());
- }
-
- @Override
public Parcelable onSaveInstanceState(Parcelable superState) {
- return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
- is24HourView(), getCurrentItemShowing());
+ return new SavedState(superState, getHour(), getMinute(),
+ is24Hour(), getCurrentItemShowing());
}
@Override
@@ -520,12 +509,6 @@
}
@Override
- public void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
- mTempCalendar = Calendar.getInstance(locale);
- }
-
- @Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
return true;
@@ -534,13 +517,13 @@
@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
int flags = DateUtils.FORMAT_SHOW_TIME;
- if (mIs24HourView) {
+ if (mIs24Hour) {
flags |= DateUtils.FORMAT_24HOUR;
} else {
flags |= DateUtils.FORMAT_12HOUR;
}
- mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
- mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
+ mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
+ mTempCalendar.set(Calendar.MINUTE, getMinute());
String selectedDate = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
event.getText().add(selectedDate);
@@ -559,8 +542,7 @@
private void onTimeChanged() {
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator,
- getCurrentHour(), getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
}
@@ -666,25 +648,29 @@
}
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(), getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
}
/**
* Converts hour-of-day (0-23) time into a localized hour number.
+ * <p>
+ * The localized value may be in the range (0-23), (1-24), (0-11), or
+ * (1-12) depending on the locale. This method does not handle leading
+ * zeroes.
*
* @param hourOfDay the hour-of-day (0-23)
* @return a localized hour number
*/
private int getLocalizedHour(int hourOfDay) {
- if (!mIs24HourView) {
+ if (!mIs24Hour) {
// Convert to hour-of-am-pm.
hourOfDay %= 12;
}
if (!mHourFormatStartsAtZero && hourOfDay == 0) {
// Convert to clock-hour (either of-day or of-am-pm).
- hourOfDay = mIs24HourView ? 24 : 12;
+ hourOfDay = mIs24Hour ? 24 : 12;
}
return hourOfDay;
@@ -716,8 +702,8 @@
* separator as the character which is just after the hour marker in the returned pattern.
*/
private void updateHeaderSeparator() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
- (mIs24HourView) ? "Hm" : "hm");
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
+ (mIs24Hour) ? "Hm" : "hm");
final String separatorText;
// See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
final char[] hourFormats = {'H', 'h', 'K', 'k'};
@@ -819,14 +805,14 @@
private final Runnable mCommitHour = new Runnable() {
@Override
public void run() {
- setCurrentHour(mHourView.getValue());
+ setHour(mHourView.getValue());
}
};
private final Runnable mCommitMinute = new Runnable() {
@Override
public void run() {
- setCurrentMinute(mMinuteView.getValue());
+ setMinute(mMinuteView.getValue());
}
};
@@ -894,8 +880,12 @@
public boolean onTouch(View view, MotionEvent motionEvent) {
final int actionMasked = motionEvent.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
- mInitialTouchTarget = findNearestChild((ViewGroup) view,
- (int) motionEvent.getX(), (int) motionEvent.getY());
+ if (view instanceof ViewGroup) {
+ mInitialTouchTarget = findNearestChild((ViewGroup) view,
+ (int) motionEvent.getX(), (int) motionEvent.getY());
+ } else {
+ mInitialTouchTarget = null;
+ }
}
final View child = mInitialTouchTarget;
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 8741cc3..863d409 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -17,7 +17,6 @@
package android.widget;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -47,11 +46,6 @@
private static final boolean DEFAULT_ENABLED_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
- // state
- private boolean mIs24HourView;
- private boolean mIsAm;
-
- // ui components
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
@@ -68,11 +62,15 @@
private final String[] mAmPmStrings;
+ private final Calendar mTempCalendar;
+
private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
- private Calendar mTempCalendar;
private boolean mHourWithTwoDigit;
private char mHourFormat;
+ private boolean mIs24HourView;
+ private boolean mIsAm;
+
public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
@@ -92,7 +90,7 @@
mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
updateInputState();
- if (!is24HourView()) {
+ if (!is24Hour()) {
if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) ||
(oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
mIsAm = !mIsAm;
@@ -124,14 +122,14 @@
int maxValue = mMinuteSpinner.getMaxValue();
if (oldVal == maxValue && newVal == minValue) {
int newHour = mHourSpinner.getValue() + 1;
- if (!is24HourView() && newHour == HOURS_IN_HALF_DAY) {
+ if (!is24Hour() && newHour == HOURS_IN_HALF_DAY) {
mIsAm = !mIsAm;
updateAmPmControl();
}
mHourSpinner.setValue(newHour);
} else if (oldVal == minValue && newVal == maxValue) {
int newHour = mHourSpinner.getValue() - 1;
- if (!is24HourView() && newHour == HOURS_IN_HALF_DAY - 1) {
+ if (!is24Hour() && newHour == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
@@ -204,8 +202,9 @@
updateAmPmControl();
// set to current time
- setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
- setCurrentMinute(mTempCalendar.get(Calendar.MINUTE));
+ mTempCalendar = Calendar.getInstance(mLocale);
+ setHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
+ setMinute(mTempCalendar.get(Calendar.MINUTE));
if (!isEnabled()) {
setEnabled(false);
@@ -221,7 +220,7 @@
}
private void getHourFormatData() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
(mIs24HourView) ? "Hm" : "hm");
final int lengthPattern = bestDateTimePattern.length();
mHourWithTwoDigit = false;
@@ -241,7 +240,7 @@
}
private boolean isAmPmAtStart() {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
"hm" /* skeleton */);
return bestDateTimePattern.startsWith("a");
@@ -257,7 +256,7 @@
*/
private void setDividerText() {
final String skeleton = (mIs24HourView) ? "Hm" : "hm";
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
skeleton);
final String separatorText;
int hourIndex = bestDateTimePattern.lastIndexOf('H');
@@ -279,16 +278,16 @@
}
@Override
- public void setCurrentHour(int currentHour) {
- setCurrentHour(currentHour, true);
+ public void setHour(int hour) {
+ setCurrentHour(hour, true);
}
private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
// why was Integer used in the first place?
- if (currentHour == getCurrentHour()) {
+ if (currentHour == getHour()) {
return;
}
- if (!is24HourView()) {
+ if (!is24Hour()) {
// convert [0,23] ordinal to wall clock display
if (currentHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
@@ -310,9 +309,9 @@
}
@Override
- public int getCurrentHour() {
+ public int getHour() {
int currentHour = mHourSpinner.getValue();
- if (is24HourView()) {
+ if (is24Hour()) {
return currentHour;
} else if (mIsAm) {
return currentHour % HOURS_IN_HALF_DAY;
@@ -322,28 +321,27 @@
}
@Override
- public void setCurrentMinute(int currentMinute) {
- if (currentMinute == getCurrentMinute()) {
+ public void setMinute(int minute) {
+ if (minute == getMinute()) {
return;
}
- mMinuteSpinner.setValue(currentMinute);
+ mMinuteSpinner.setValue(minute);
onTimeChanged();
}
@Override
- public int getCurrentMinute() {
+ public int getMinute() {
return mMinuteSpinner.getValue();
}
- @Override
- public void setIs24HourView(boolean is24HourView) {
- if (mIs24HourView == is24HourView) {
+ public void setIs24Hour(boolean is24Hour) {
+ if (mIs24HourView == is24Hour) {
return;
}
// cache the current hour since spinner range changes and BEFORE changing mIs24HourView!!
- int currentHour = getCurrentHour();
+ int currentHour = getHour();
// Order is important here.
- mIs24HourView = is24HourView;
+ mIs24HourView = is24Hour;
getHourFormatData();
updateHourControl();
// set value after spinner range is updated
@@ -353,7 +351,7 @@
}
@Override
- public boolean is24HourView() {
+ public boolean is24Hour() {
return mIs24HourView;
}
@@ -388,20 +386,15 @@
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- setCurrentLocale(newConfig.locale);
- }
-
- @Override
public Parcelable onSaveInstanceState(Parcelable superState) {
- return new SavedState(superState, getCurrentHour(), getCurrentMinute());
+ return new SavedState(superState, getHour(), getMinute());
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
- setCurrentHour(ss.getHour());
- setCurrentMinute(ss.getMinute());
+ setHour(ss.getHour());
+ setMinute(ss.getMinute());
}
@Override
@@ -418,8 +411,8 @@
} else {
flags |= DateUtils.FORMAT_12HOUR;
}
- mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
- mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
+ mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
+ mTempCalendar.set(Calendar.MINUTE, getMinute());
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
mTempCalendar.getTimeInMillis(), flags);
event.getText().add(selectedDateUtterance);
@@ -447,7 +440,7 @@
}
private void updateAmPmControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
if (mAmPmSpinner != null) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
@@ -466,27 +459,16 @@
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
- /**
- * Sets the current locale.
- *
- * @param locale The current locale.
- */
- @Override
- public void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
- mTempCalendar = Calendar.getInstance(locale);
- }
-
private void onTimeChanged() {
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(),
- getCurrentMinute());
+ mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(),
+ getMinute());
}
}
private void updateHourControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
// 'k' means 1-24 hour
if (mHourFormat == 'k') {
mHourSpinner.setMinValue(1);
@@ -509,7 +491,7 @@
}
private void updateMinuteControl() {
- if (is24HourView()) {
+ if (is24Hour()) {
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
} else {
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index acbf5eb..6e56513 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1368,6 +1368,11 @@
}
@Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 89e59f9..96624d2 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -38,8 +38,6 @@
private OnYearSelectedListener mOnYearSelectedListener;
- private long mCurrentTimeMillis;
-
public YearPickerView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listViewStyle);
}
@@ -79,10 +77,6 @@
mOnYearSelectedListener = listener;
}
- public void setDate(long currentTimeMillis) {
- mCurrentTimeMillis = currentTimeMillis;
- }
-
/**
* Sets the currently selected year. Jumps immediately to the new year.
*
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.aidl b/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
new file mode 100644
index 0000000..529527b
--- /dev/null
+++ b/core/java/com/android/internal/app/EphemeralResolveInfo.aidl
@@ -0,0 +1,19 @@
+/*
+** 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.
+*/
+
+package com.android.internal.app;
+
+parcelable EphemeralResolveInfo;
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.java b/core/java/com/android/internal/app/EphemeralResolveInfo.java
new file mode 100644
index 0000000..0e7ef05
--- /dev/null
+++ b/core/java/com/android/internal/app/EphemeralResolveInfo.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information that is returned when resolving ephemeral
+ * applications.
+ */
+public final class EphemeralResolveInfo implements Parcelable {
+ public static final String SHA_ALGORITHM = "SHA-256";
+ private byte[] mDigestBytes;
+ private int mDigestPrefix;
+ private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+ public EphemeralResolveInfo(Uri uri, List<IntentFilter> filters) {
+ generateDigest(uri);
+ mFilters.addAll(filters);
+ }
+
+ private EphemeralResolveInfo(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public byte[] getDigestBytes() {
+ return mDigestBytes;
+ }
+
+ public int getDigestPrefix() {
+ return mDigestPrefix;
+ }
+
+ public List<IntentFilter> getFilters() {
+ return mFilters;
+ }
+
+ private void generateDigest(Uri uri) {
+ try {
+ final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
+ final byte[] hostBytes = uri.getHost().getBytes();
+ final byte[] digestBytes = digest.digest(hostBytes);
+ mDigestBytes = digestBytes;
+ mDigestPrefix =
+ digestBytes[0] << 24
+ | digestBytes[1] << 16
+ | digestBytes[2] << 8
+ | digestBytes[3] << 0;
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("could not find digest algorithm");
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ if (mDigestBytes == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(mDigestBytes.length);
+ out.writeByteArray(mDigestBytes);
+ }
+ out.writeInt(mDigestPrefix);
+ out.writeList(mFilters);
+ }
+
+ private void readFromParcel(Parcel in) {
+ int digestBytesSize = in.readInt();
+ if (digestBytesSize > 0) {
+ mDigestBytes = new byte[digestBytesSize];
+ in.readByteArray(mDigestBytes);
+ }
+ mDigestPrefix = in.readInt();
+ in.readList(mFilters, null /*loader*/);
+ }
+
+ public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
+ = new Parcelable.Creator<EphemeralResolveInfo>() {
+ public EphemeralResolveInfo createFromParcel(Parcel in) {
+ return new EphemeralResolveInfo(in);
+ }
+
+ public EphemeralResolveInfo[] newArray(int size) {
+ return new EphemeralResolveInfo[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/app/EphemeralResolverService.java b/core/java/com/android/internal/app/EphemeralResolverService.java
new file mode 100644
index 0000000..65530f2
--- /dev/null
+++ b/core/java/com/android/internal/app/EphemeralResolverService.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * Base class for implementing the resolver service.
+ * @hide
+ */
+public abstract class EphemeralResolverService extends Service {
+ public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO";
+ public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE";
+ private Handler mHandler;
+
+ /**
+ * Called to retrieve resolve info for ephemeral applications.
+ *
+ * @param digestPrefix The hash prefix of the ephemeral's domain.
+ */
+ protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList(int digestPrefix);
+
+ @Override
+ protected final void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ mHandler = new ServiceHandler(base.getMainLooper());
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IEphemeralResolver.Stub() {
+ @Override
+ public void getEphemeralResolveInfoList(
+ IRemoteCallback callback, int digestPrefix, int sequence) {
+ mHandler.obtainMessage(ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO,
+ digestPrefix, sequence, callback)
+ .sendToTarget();
+ }
+ };
+ }
+
+ private final class ServiceHandler extends Handler {
+ public static final int MSG_GET_EPHEMERAL_RESOLVE_INFO = 1;
+
+ public ServiceHandler(Looper looper) {
+ super(looper, null /*callback*/, true /*async*/);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void handleMessage(Message message) {
+ final int action = message.what;
+ switch (action) {
+ case MSG_GET_EPHEMERAL_RESOLVE_INFO: {
+ final IRemoteCallback callback = (IRemoteCallback) message.obj;
+ final List<EphemeralResolveInfo> resolveInfo =
+ getEphemeralResolveInfoList(message.arg1);
+ final Bundle data = new Bundle();
+ data.putInt(EXTRA_SEQUENCE, message.arg2);
+ data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
+ try {
+ callback.sendResult(data);
+ } catch (RemoteException e) {
+ }
+ } break;
+
+ default: {
+ throw new IllegalArgumentException("Unknown message: " + action);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/IAppOpsCallback.aidl b/core/java/com/android/internal/app/IAppOpsCallback.aidl
index afbc609..5fdc920 100644
--- a/core/java/com/android/internal/app/IAppOpsCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl
@@ -19,5 +19,5 @@
// This interface is also used by native code, so must
// be kept in sync with frameworks/native/include/binder/IAppOpsCallback.h
oneway interface IAppOpsCallback {
- void opChanged(int op, String packageName);
+ void opChanged(int op, int uid, String packageName);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/core/java/com/android/internal/app/IEphemeralResolver.aidl
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to core/java/com/android/internal/app/IEphemeralResolver.aidl
index f187178..40429ee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/core/java/com/android/internal/app/IEphemeralResolver.aidl
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.internal.app;
-import com.android.systemui.recents.events.EventBus;
+import android.content.Intent;
+import android.os.IRemoteCallback;
-/**
- * This is sent when the window animation into Recents starts.
- */
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+oneway interface IEphemeralResolver {
+ void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/core/java/com/android/internal/app/SystemUserHomeActivity.java
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to core/java/com/android/internal/app/SystemUserHomeActivity.java
index f187178..26fbf6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/core/java/com/android/internal/app/SystemUserHomeActivity.java
@@ -11,16 +11,16 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.recents.events.activity;
+package com.android.internal.app;
-import com.android.systemui.recents.events.EventBus;
+import android.app.Activity;
/**
- * This is sent when the window animation into Recents starts.
+ * Placeholder home activity, which is always installed on the system user. At least one home
+ * activity must be present and enabled in order for the system to boot.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+public class SystemUserHomeActivity extends Activity {
}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 2c5e50c..c992c70 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -30,6 +30,10 @@
public static final int QS_LOCK_TILE = 257;
public static final int QS_USER_TILE = 258;
public static final int QS_BATTERY_TILE = 259;
+ public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
+ public static final int ACTION_ZEN_ALLOW_PEEK = 261;
+ public static final int ACTION_ZEN_ALLOW_LIGHTS = 262;
+ public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index e26b27d..c067da7 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -17,13 +17,19 @@
package com.android.internal.os;
+import android.os.ShellCommand;
+
import java.io.PrintStream;
public abstract class BaseCommand {
- protected String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
+ final protected ShellCommand mArgs = new ShellCommand() {
+ @Override public int onCommand(String cmd) {
+ return 0;
+ }
+ @Override public void onHelp() {
+ }
+ };
// These are magic strings understood by the Eclipse plugin.
public static final String FATAL_ERROR_CODE = "Error type 1";
@@ -39,9 +45,7 @@
return;
}
- mArgs = args;
- mNextArg = 0;
- mCurArgData = null;
+ mArgs.init(null, null, null, null, args, 0);
try {
onRun();
@@ -87,32 +91,7 @@
* starts with '-'. If the next argument is not an option, null is returned.
*/
public String nextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
+ return mArgs.getNextOption();
}
/**
@@ -120,15 +99,7 @@
* no arguments left, return null.
*/
public String nextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mNextArg < mArgs.length) {
- return mArgs[mNextArg++];
- } else {
- return null;
- }
+ return mArgs.getNextArg();
}
/**
@@ -136,11 +107,6 @@
* no arguments left, throws an IllegalArgumentException to report this to the user.
*/
public String nextArgRequired() {
- String arg = nextArg();
- if (arg == null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
+ return mArgs.getNextArgRequired();
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f73df00..9391c60 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 135 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 136 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1975,8 +1975,14 @@
private int buildBatteryLevelInt(HistoryItem h) {
return ((((int)h.batteryLevel)<<25)&0xfe000000)
- | ((((int)h.batteryTemperature)<<14)&0x01ff8000)
- | ((((int)h.batteryVoltage)<<1)&0x00007fff);
+ | ((((int)h.batteryTemperature)<<15)&0x01ff8000)
+ | ((((int)h.batteryVoltage)<<1)&0x00007ffe);
+ }
+
+ private void readBatteryLevelInt(int batteryLevelInt, HistoryItem out) {
+ out.batteryLevel = (byte)((batteryLevelInt & 0xfe000000) >>> 25);
+ out.batteryTemperature = (short)((batteryLevelInt & 0x01ff8000) >>> 15);
+ out.batteryVoltage = (char)((batteryLevelInt & 0x00007ffe) >>> 1);
}
private int buildStateInt(HistoryItem h) {
@@ -2117,9 +2123,7 @@
final int batteryLevelInt;
if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) {
batteryLevelInt = src.readInt();
- cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f);
- cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21);
- cur.batteryVoltage = (char)(batteryLevelInt&0x3fff);
+ readBatteryLevelInt(batteryLevelInt, cur);
cur.numReadInts += 1;
if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x"
+ Integer.toHexString(batteryLevelInt)
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 197004c..8186378 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -39,10 +39,8 @@
public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
/** Enable logging of third-party JNI activity. */
public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
- /** enable the JIT compiler */
- public static final int DEBUG_ENABLE_JIT = 1 << 5;
/** Force generation of native debugging information. */
- public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 6;
+ public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 5;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = 0;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 3e86fac..a40f9a8 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -322,7 +322,7 @@
/**
* From --enable-debugger, --enable-checkjni, --enable-assert,
- * --enable-safemode, --enable-jit, --generate-debug-info and --enable-jni-logging.
+ * --enable-safemode, --generate-debug-info and --enable-jni-logging.
*/
int debugFlags;
@@ -432,8 +432,6 @@
debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
} else if (arg.equals("--enable-checkjni")) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- } else if (arg.equals("--enable-jit")) {
- debugFlags |= Zygote.DEBUG_ENABLE_JIT;
} else if (arg.equals("--generate-debug-info")) {
debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
} else if (arg.equals("--enable-jni-logging")) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f78d8d8..8e318a2 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -346,7 +346,7 @@
long startTime = SystemClock.uptimeMillis();
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
- int N = preloadDrawables(runtime, ar);
+ int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
@@ -354,10 +354,21 @@
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
- N = preloadColorStateLists(runtime, ar);
+ N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
+
+ if (mResources.getBoolean(
+ com.android.internal.R.bool.config_freeformWindowManagement)) {
+ startTime = SystemClock.uptimeMillis();
+ ar = mResources.obtainTypedArray(
+ com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
+ N = preloadDrawables(ar);
+ ar.recycle();
+ Log.i(TAG, "...preloaded " + N + " resource in "
+ + (SystemClock.uptimeMillis() - startTime) + "ms.");
+ }
}
mResources.finishPreloading();
} catch (RuntimeException e) {
@@ -365,7 +376,7 @@
}
}
- private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
+ private static int preloadColorStateLists(TypedArray ar) {
int N = ar.length();
for (int i=0; i<N; i++) {
int id = ar.getResourceId(i, 0);
@@ -385,7 +396,7 @@
}
- private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
+ private static int preloadDrawables(TypedArray ar) {
int N = ar.length();
for (int i=0; i<N; i++) {
int id = ar.getResourceId(i, 0);
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
new file mode 100644
index 0000000..b101733
--- /dev/null
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.policy;
+
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+import android.view.View;
+
+/**
+ * The thread which draws a fill in background while the app is resizing in areas where the app
+ * content draw is lagging behind the resize operation.
+ * It starts with the creation and it ends once someone calls destroy().
+ * Any size changes can be passed by a call to setTargetRect will passed to the thread and
+ * executed via the Choreographer.
+ * @hide
+ */
+public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback {
+
+ private DecorView mDecorView;
+
+ // This is containing the last requested size by a resize command. Note that this size might
+ // or might not have been applied to the output already.
+ private final Rect mTargetRect = new Rect();
+
+ // The render nodes for the multi threaded renderer.
+ private ThreadedRenderer mRenderer;
+ private RenderNode mFrameAndBackdropNode;
+
+ private final Rect mOldTargetRect = new Rect();
+ private final Rect mNewTargetRect = new Rect();
+ private Choreographer mChoreographer;
+
+ // Cached size values from the last render for the case that the view hierarchy is gone
+ // during a configuration change.
+ private int mLastContentWidth;
+ private int mLastContentHeight;
+ private int mLastCaptionHeight;
+ private int mLastXOffset;
+ private int mLastYOffset;
+
+ // Whether to report when next frame is drawn or not.
+ private boolean mReportNextDraw;
+
+ private Drawable mCaptionBackgroundDrawable;
+ private Drawable mResizingBackgroundDrawable;
+
+ public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
+ Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable) {
+ setName("ResizeFrame");
+
+ mRenderer = renderer;
+ onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable);
+
+ // Create a render node for the content and frame backdrop
+ // which can be resized independently from the content.
+ mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
+
+ mRenderer.addRenderNode(mFrameAndBackdropNode, true);
+
+ // Set the initial bounds and draw once so that we do not get a broken frame.
+ mTargetRect.set(initialBounds);
+ synchronized (this) {
+ changeWindowSizeLocked(initialBounds);
+ }
+
+ // Kick off our draw thread.
+ start();
+ }
+
+ void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
+ Drawable captionBackgroundDrawableDrawable) {
+ mDecorView = decorView;
+ mResizingBackgroundDrawable = resizingBackgroundDrawable;
+ mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
+ }
+
+ /**
+ * Call this function asynchronously when the window size has been changed. The change will
+ * be picked up once per frame and the frame will be re-rendered accordingly.
+ * @param newTargetBounds The new target bounds.
+ */
+ public void setTargetRect(Rect newTargetBounds) {
+ synchronized (this) {
+ mTargetRect.set(newTargetBounds);
+ // Notify of a bounds change.
+ pingRenderLocked();
+ }
+ }
+
+ /**
+ * The window got replaced due to a configuration change.
+ */
+ public void onConfigurationChange() {
+ synchronized (this) {
+ if (mRenderer != null) {
+ // Enforce a window redraw.
+ mOldTargetRect.set(0, 0, 0, 0);
+ pingRenderLocked();
+ }
+ }
+ }
+
+ /**
+ * All resources of the renderer will be released. This function can be called from the
+ * the UI thread as well as the renderer thread.
+ */
+ public void releaseRenderer() {
+ synchronized (this) {
+ if (mRenderer != null) {
+ // Invalidate the current content bounds.
+ mRenderer.setContentDrawBounds(0, 0, 0, 0);
+
+ // Remove the render node again
+ // (see comment above - better to do that only once).
+ mRenderer.removeRenderNode(mFrameAndBackdropNode);
+
+ mRenderer = null;
+
+ // Exit the renderer loop.
+ pingRenderLocked();
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ Looper.prepare();
+ synchronized (this) {
+ mChoreographer = Choreographer.getInstance();
+
+ // Draw at least once.
+ mChoreographer.postFrameCallback(this);
+ }
+ Looper.loop();
+ } finally {
+ releaseRenderer();
+ }
+ synchronized (this) {
+ // Make sure no more messages are being sent.
+ mChoreographer = null;
+ }
+ }
+
+ /**
+ * The implementation of the FrameCallback.
+ * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
+ * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000}
+ */
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ synchronized (this) {
+ if (mRenderer == null) {
+ reportDrawIfNeeded();
+ // Tell the looper to stop. We are done.
+ Looper.myLooper().quit();
+ return;
+ }
+ mNewTargetRect.set(mTargetRect);
+ if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) {
+ mOldTargetRect.set(mNewTargetRect);
+ changeWindowSizeLocked(mNewTargetRect);
+ }
+ }
+ }
+
+ /**
+ * The content is about to be drawn and we got the location of where it will be shown.
+ * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
+ * if the previous call was ignored since the size was unknown.
+ * @param xOffset The x offset where the content is drawn to.
+ * @param yOffset The y offset where the content is drawn to.
+ * @param xSize The width size of the content. This should not be 0.
+ * @param ySize The height of the content.
+ * @return true if a frame should be requested after the content is drawn; false otherwise.
+ */
+ public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
+ synchronized (this) {
+ final boolean firstCall = mLastContentWidth == 0;
+ // The current content buffer is drawn here.
+ mLastContentWidth = xSize;
+ mLastContentHeight = ySize - mLastCaptionHeight;
+ mLastXOffset = xOffset;
+ mLastYOffset = yOffset;
+
+ mRenderer.setContentDrawBounds(
+ mLastXOffset,
+ mLastYOffset,
+ mLastXOffset + mLastContentWidth,
+ mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ // If this was the first call and changeWindowSizeLocked got already called prior
+ // to us, we should re-issue a changeWindowSizeLocked now.
+ return firstCall
+ && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption());
+ }
+ }
+
+ public void onRequestDraw(boolean reportNextDraw) {
+ synchronized (this) {
+ mReportNextDraw = reportNextDraw;
+ mOldTargetRect.set(0, 0, 0, 0);
+ pingRenderLocked();
+ }
+ }
+
+ /**
+ * Resizing the frame to fit the new window size.
+ * @param newBounds The window bounds which needs to be drawn.
+ */
+ private void changeWindowSizeLocked(Rect newBounds) {
+
+ // While a configuration change is taking place the view hierarchy might become
+ // inaccessible. For that case we remember the previous metrics to avoid flashes.
+ // Note that even when there is no visible caption, the caption child will exist.
+ final int captionHeight = mDecorView.getCaptionHeight();
+ // The caption height will probably never dynamically change while we are resizing.
+ // Once set to something other then 0 it should be kept that way.
+ if (captionHeight != 0) {
+ // Remember the height of the caption.
+ mLastCaptionHeight = captionHeight;
+ }
+
+ // Make sure that the other thread has already prepared the render draw calls for the
+ // content. If any size is 0, we have to wait for it to be drawn first.
+ if ((mLastCaptionHeight == 0 && mDecorView.isShowingCaption()) ||
+ mLastContentWidth == 0 || mLastContentHeight == 0) {
+ return;
+ }
+
+ // Since the surface is spanning the entire screen, we have to add the start offset of
+ // the bounds to get to the surface location.
+ final int left = mLastXOffset + newBounds.left;
+ final int top = mLastYOffset + newBounds.top;
+ final int width = newBounds.width();
+ final int height = newBounds.height();
+
+ mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
+
+ // Draw the caption and content backdrops in to our render node.
+ final DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
+ mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ mCaptionBackgroundDrawable.draw(canvas);
+
+ // The backdrop: clear everything with the background. Clipping is done elsewhere.
+ mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
+ mResizingBackgroundDrawable.draw(canvas);
+ mFrameAndBackdropNode.end(canvas);
+
+ // We need to render the node explicitly
+ mRenderer.drawRenderNode(mFrameAndBackdropNode);
+
+ reportDrawIfNeeded();
+ }
+
+ /** Notify view root that a frame has been drawn by us, if it has requested so. */
+ private void reportDrawIfNeeded() {
+ if (mReportNextDraw) {
+ if (mDecorView.isAttachedToWindow()) {
+ mDecorView.getViewRootImpl().reportDrawFinish();
+ }
+ mReportNextDraw = false;
+ }
+ }
+
+ /**
+ * Sends a message to the renderer to wake up and perform the next action which can be
+ * either the next rendering or the self destruction if mRenderer is null.
+ * Note: This call must be synchronized.
+ */
+ private void pingRenderLocked() {
+ if (mChoreographer != null) {
+ mChoreographer.postFrameCallback(this);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
new file mode 100644
index 0000000..27fe03c
--- /dev/null
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -0,0 +1,1936 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.policy;
+
+import com.android.internal.R;
+import com.android.internal.view.FloatingActionMode;
+import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.StandaloneActionMode;
+import com.android.internal.view.menu.ContextMenuBuilder;
+import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.BackgroundFallback;
+import com.android.internal.widget.DecorCaptionView;
+import com.android.internal.widget.FloatingToolbar;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.InputQueue;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowCallbacks;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getMode;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+/** @hide */
+public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
+ private static final String TAG = "DecorView";
+
+ private static final boolean SWEEP_OPEN_MENU = false;
+
+ // The height of a window which has focus in DIP.
+ private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
+ // The height of a window which has not in DIP.
+ private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
+
+ // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
+ // size calculation takes the shadow size into account. We set the elevation currently
+ // to max until the first layout command has been executed.
+ private boolean mAllowUpdateElevation = false;
+
+ private boolean mElevationAdjustedForStack = false;
+
+ int mDefaultOpacity = PixelFormat.OPAQUE;
+
+ /** The feature ID of the panel, or -1 if this is the application's DecorView */
+ private final int mFeatureId;
+
+ private final Rect mDrawingBounds = new Rect();
+
+ private final Rect mBackgroundPadding = new Rect();
+
+ private final Rect mFramePadding = new Rect();
+
+ private final Rect mFrameOffsets = new Rect();
+
+ private boolean mHasCaption = false;
+
+ private boolean mChanging;
+
+ private Drawable mMenuBackground;
+ private boolean mWatchingForMenu;
+ private int mDownY;
+
+ ActionMode mPrimaryActionMode;
+ private ActionMode mFloatingActionMode;
+ private ActionBarContextView mPrimaryActionModeView;
+ private PopupWindow mPrimaryActionModePopup;
+ private Runnable mShowPrimaryActionModePopup;
+ private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
+ private View mFloatingActionModeOriginatingView;
+ private FloatingToolbar mFloatingToolbar;
+ private ObjectAnimator mFadeAnim;
+
+ // View added at runtime to draw under the status bar area
+ private View mStatusGuard;
+ // View added at runtime to draw under the navigation bar area
+ private View mNavigationGuard;
+
+ private final ColorViewState mStatusColorViewState = new ColorViewState(
+ SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+ Gravity.TOP, Gravity.LEFT,
+ Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.statusBarBackground,
+ FLAG_FULLSCREEN);
+ private final ColorViewState mNavigationColorViewState = new ColorViewState(
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+ Gravity.BOTTOM, Gravity.RIGHT,
+ Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+ com.android.internal.R.id.navigationBarBackground,
+ 0 /* hideWindowFlag */);
+
+ private final Interpolator mShowInterpolator;
+ private final Interpolator mHideInterpolator;
+ private final int mBarEnterExitDuration;
+
+ private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
+
+ private int mLastTopInset = 0;
+ private int mLastBottomInset = 0;
+ private int mLastRightInset = 0;
+ private boolean mLastHasTopStableInset = false;
+ private boolean mLastHasBottomStableInset = false;
+ private boolean mLastHasRightStableInset = false;
+ private int mLastWindowFlags = 0;
+
+ private int mRootScrollY = 0;
+
+ private PhoneWindow mWindow;
+
+ ViewGroup mContentRoot;
+
+ private Rect mTempRect;
+ private Rect mOutsets = new Rect();
+
+ // This is the caption view for the window, containing the caption and window control
+ // buttons. The visibility of this decor depends on the workspace and the window type.
+ // If the window type does not require such a view, this member might be null.
+ DecorCaptionView mDecorCaptionView;
+
+ // Stack window is currently in. Since querying and changing the stack is expensive,
+ // this is the stack value the window is currently set up for.
+ int mStackId;
+
+ private boolean mWindowResizeCallbacksAdded = false;
+
+ BackdropFrameRenderer mBackdropFrameRenderer = null;
+ private Drawable mResizingBackgroundDrawable;
+ private Drawable mCaptionBackgroundDrawable;
+
+ DecorView(Context context, int featureId, PhoneWindow window) {
+ super(context);
+ mFeatureId = featureId;
+
+ mShowInterpolator = AnimationUtils.loadInterpolator(context,
+ android.R.interpolator.linear_out_slow_in);
+ mHideInterpolator = AnimationUtils.loadInterpolator(context,
+ android.R.interpolator.fast_out_linear_in);
+
+ mBarEnterExitDuration = context.getResources().getInteger(
+ R.integer.dock_enter_exit_duration);
+
+ setWindow(window);
+ }
+
+ void setBackgroundFallback(int resId) {
+ mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
+ setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
+ }
+
+ @Override
+ public void onDraw(Canvas c) {
+ super.onDraw(c);
+ mBackgroundFallback.draw(mContentRoot, c, mWindow.mContentParent);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ final int action = event.getAction();
+ final boolean isDown = action == KeyEvent.ACTION_DOWN;
+
+ if (isDown && (event.getRepeatCount() == 0)) {
+ // First handle chording of panel key: if a panel key is held
+ // but not released, try to execute a shortcut in it.
+ if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
+ boolean handled = dispatchKeyShortcutEvent(event);
+ if (handled) {
+ return true;
+ }
+ }
+
+ // If a panel is open, perform a shortcut on it without the
+ // chorded panel key
+ if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
+ if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
+ return true;
+ }
+ }
+ }
+
+ if (!mWindow.isDestroyed()) {
+ final Window.Callback cb = mWindow.getCallback();
+ final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
+ : super.dispatchKeyEvent(event);
+ if (handled) {
+ return true;
+ }
+ }
+
+ return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
+ : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
+ }
+
+ @Override
+ public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
+ // If the panel is already prepared, then perform the shortcut using it.
+ boolean handled;
+ if (mWindow.mPreparedPanel != null) {
+ handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
+ Menu.FLAG_PERFORM_NO_CLOSE);
+ if (handled) {
+ if (mWindow.mPreparedPanel != null) {
+ mWindow.mPreparedPanel.isHandled = true;
+ }
+ return true;
+ }
+ }
+
+ // Shortcut not handled by the panel. Dispatch to the view hierarchy.
+ final Window.Callback cb = mWindow.getCallback();
+ handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
+ if (handled) {
+ return true;
+ }
+
+ // If the panel is not prepared, then we may be trying to handle a shortcut key
+ // combination such as Control+C. Temporarily prepare the panel then mark it
+ // unprepared again when finished to ensure that the panel will again be prepared
+ // the next time it is shown for real.
+ PhoneWindow.PanelFeatureState st =
+ mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
+ if (st != null && mWindow.mPreparedPanel == null) {
+ mWindow.preparePanel(st, ev);
+ handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
+ Menu.FLAG_PERFORM_NO_CLOSE);
+ st.isPrepared = false;
+ if (handled) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ final Window.Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent ev) {
+ final Window.Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ final Window.Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
+ }
+
+ public boolean superDispatchKeyEvent(KeyEvent event) {
+ // Give priority to closing action modes if applicable.
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ final int action = event.getAction();
+ // Back cancels action modes first.
+ if (mPrimaryActionMode != null) {
+ if (action == KeyEvent.ACTION_UP) {
+ mPrimaryActionMode.finish();
+ }
+ return true;
+ }
+ }
+
+ return super.dispatchKeyEvent(event);
+ }
+
+ public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
+ return super.dispatchKeyShortcutEvent(event);
+ }
+
+ public boolean superDispatchTouchEvent(MotionEvent event) {
+ return super.dispatchTouchEvent(event);
+ }
+
+ public boolean superDispatchTrackballEvent(MotionEvent event) {
+ return super.dispatchTrackballEvent(event);
+ }
+
+ public boolean superDispatchGenericMotionEvent(MotionEvent event) {
+ return super.dispatchGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return onInterceptTouchEvent(event);
+ }
+
+ private boolean isOutOfInnerBounds(int x, int y) {
+ return x < 0 || y < 0 || x > getWidth() || y > getHeight();
+ }
+
+ private boolean isOutOfBounds(int x, int y) {
+ return x < -5 || y < -5 || x > (getWidth() + 5)
+ || y > (getHeight() + 5);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ int action = event.getAction();
+ if (mHasCaption && isShowingCaption()) {
+ // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
+ // was (starting) outside the window. Window resizing events should be handled by
+ // WindowManager.
+ // TODO: Investigate how to handle the outside touch in window manager
+ // without generating these events.
+ // Currently we receive these because we need to enlarge the window's
+ // touch region so that the monitor channel receives the events
+ // in the outside touch area.
+ if (action == MotionEvent.ACTION_DOWN) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if (isOutOfInnerBounds(x, y)) {
+ return true;
+ }
+ }
+ }
+
+ if (mFeatureId >= 0) {
+ if (action == MotionEvent.ACTION_DOWN) {
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+ if (isOutOfBounds(x, y)) {
+ mWindow.closePanel(mFeatureId);
+ return true;
+ }
+ }
+ }
+
+ if (!SWEEP_OPEN_MENU) {
+ return false;
+ }
+
+ if (mFeatureId >= 0) {
+ if (action == MotionEvent.ACTION_DOWN) {
+ Log.i(TAG, "Watchiing!");
+ mWatchingForMenu = true;
+ mDownY = (int) event.getY();
+ return false;
+ }
+
+ if (!mWatchingForMenu) {
+ return false;
+ }
+
+ int y = (int)event.getY();
+ if (action == MotionEvent.ACTION_MOVE) {
+ if (y > (mDownY+30)) {
+ Log.i(TAG, "Closing!");
+ mWindow.closePanel(mFeatureId);
+ mWatchingForMenu = false;
+ return true;
+ }
+ } else if (action == MotionEvent.ACTION_UP) {
+ mWatchingForMenu = false;
+ }
+
+ return false;
+ }
+
+ //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
+ // + " (in " + getHeight() + ")");
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ int y = (int)event.getY();
+ if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
+ Log.i(TAG, "Watching!");
+ mWatchingForMenu = true;
+ }
+ return false;
+ }
+
+ if (!mWatchingForMenu) {
+ return false;
+ }
+
+ int y = (int)event.getY();
+ if (action == MotionEvent.ACTION_MOVE) {
+ if (y < (getHeight()-30)) {
+ Log.i(TAG, "Opening!");
+ mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
+ mWatchingForMenu = false;
+ return true;
+ }
+ } else if (action == MotionEvent.ACTION_UP) {
+ mWatchingForMenu = false;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void sendAccessibilityEvent(int eventType) {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+
+ // if we are showing a feature that should be announced and one child
+ // make this child the event source since this is the feature itself
+ // otherwise the callback will take over and announce its client
+ if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
+ mFeatureId == Window.FEATURE_CONTEXT_MENU ||
+ mFeatureId == Window.FEATURE_PROGRESS ||
+ mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
+ && getChildCount() == 1) {
+ getChildAt(0).sendAccessibilityEvent(eventType);
+ } else {
+ super.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
+ final Window.Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed()) {
+ if (cb.dispatchPopulateAccessibilityEvent(event)) {
+ return true;
+ }
+ }
+ return super.dispatchPopulateAccessibilityEventInternal(event);
+ }
+
+ @Override
+ protected boolean setFrame(int l, int t, int r, int b) {
+ boolean changed = super.setFrame(l, t, r, b);
+ if (changed) {
+ final Rect drawingBounds = mDrawingBounds;
+ getDrawingRect(drawingBounds);
+
+ Drawable fg = getForeground();
+ if (fg != null) {
+ final Rect frameOffsets = mFrameOffsets;
+ drawingBounds.left += frameOffsets.left;
+ drawingBounds.top += frameOffsets.top;
+ drawingBounds.right -= frameOffsets.right;
+ drawingBounds.bottom -= frameOffsets.bottom;
+ fg.setBounds(drawingBounds);
+ final Rect framePadding = mFramePadding;
+ drawingBounds.left += framePadding.left - frameOffsets.left;
+ drawingBounds.top += framePadding.top - frameOffsets.top;
+ drawingBounds.right -= framePadding.right - frameOffsets.right;
+ drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
+ }
+
+ Drawable bg = getBackground();
+ if (bg != null) {
+ bg.setBounds(drawingBounds);
+ }
+
+ if (SWEEP_OPEN_MENU) {
+ if (mMenuBackground == null && mFeatureId < 0
+ && mWindow.getAttributes().height
+ == WindowManager.LayoutParams.MATCH_PARENT) {
+ mMenuBackground = getContext().getDrawable(
+ R.drawable.menu_background);
+ }
+ if (mMenuBackground != null) {
+ mMenuBackground.setBounds(drawingBounds.left,
+ drawingBounds.bottom-6, drawingBounds.right,
+ drawingBounds.bottom+20);
+ }
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+
+ final int widthMode = getMode(widthMeasureSpec);
+ final int heightMode = getMode(heightMeasureSpec);
+
+ boolean fixedWidth = false;
+ if (widthMode == AT_MOST) {
+ final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
+ : mWindow.mFixedWidthMajor;
+ if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
+ final int w;
+ if (tvw.type == TypedValue.TYPE_DIMENSION) {
+ w = (int) tvw.getDimension(metrics);
+ } else if (tvw.type == TypedValue.TYPE_FRACTION) {
+ w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
+ } else {
+ w = 0;
+ }
+
+ if (w > 0) {
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(w, widthSize), EXACTLY);
+ fixedWidth = true;
+ }
+ }
+ }
+
+ if (heightMode == AT_MOST) {
+ final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
+ : mWindow.mFixedHeightMinor;
+ if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
+ final int h;
+ if (tvh.type == TypedValue.TYPE_DIMENSION) {
+ h = (int) tvh.getDimension(metrics);
+ } else if (tvh.type == TypedValue.TYPE_FRACTION) {
+ h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
+ } else {
+ h = 0;
+ }
+ if (h > 0) {
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(h, heightSize), EXACTLY);
+ }
+ }
+ }
+
+ getOutsets(mOutsets);
+ if (mOutsets.top > 0 || mOutsets.bottom > 0) {
+ int mode = MeasureSpec.getMode(heightMeasureSpec);
+ if (mode != MeasureSpec.UNSPECIFIED) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ height + mOutsets.top + mOutsets.bottom, mode);
+ }
+ }
+ if (mOutsets.left > 0 || mOutsets.right > 0) {
+ int mode = MeasureSpec.getMode(widthMeasureSpec);
+ if (mode != MeasureSpec.UNSPECIFIED) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ width + mOutsets.left + mOutsets.right, mode);
+ }
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = getMeasuredWidth();
+ boolean measure = false;
+
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
+
+ if (!fixedWidth && widthMode == AT_MOST) {
+ final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
+ if (tv.type != TypedValue.TYPE_NULL) {
+ final int min;
+ if (tv.type == TypedValue.TYPE_DIMENSION) {
+ min = (int)tv.getDimension(metrics);
+ } else if (tv.type == TypedValue.TYPE_FRACTION) {
+ min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+ } else {
+ min = 0;
+ }
+
+ if (width < min) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
+ measure = true;
+ }
+ }
+ }
+
+ // TODO: Support height?
+
+ if (measure) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ getOutsets(mOutsets);
+ if (mOutsets.left > 0) {
+ offsetLeftAndRight(-mOutsets.left);
+ }
+ if (mOutsets.top > 0) {
+ offsetTopAndBottom(-mOutsets.top);
+ }
+
+ // If the application changed its SystemUI metrics, we might also have to adapt
+ // our shadow elevation.
+ updateElevation();
+ mAllowUpdateElevation = true;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+
+ if (mMenuBackground != null) {
+ mMenuBackground.draw(canvas);
+ }
+ }
+
+ @Override
+ public boolean showContextMenuForChild(View originalView) {
+ // Reuse the context menu builder
+ if (mWindow.mContextMenu == null) {
+ mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+ mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
+ } else {
+ mWindow.mContextMenu.clearAll();
+ }
+
+ final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
+ originalView.getWindowToken());
+ if (helper != null) {
+ helper.setPresenterCallback(mWindow.mContextMenuCallback);
+ } else if (mWindow.mContextMenuHelper != null) {
+ // No menu to show, but if we have a menu currently showing it just became blank.
+ // Close it.
+ mWindow.mContextMenuHelper.dismiss();
+ }
+ mWindow.mContextMenuHelper = helper;
+ return helper != null;
+ }
+
+ @Override
+ public boolean showContextMenuForChild(View originalView, float x, float y) {
+ // Reuse the context menu builder
+ if (mWindow.mContextMenu == null) {
+ mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+ mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
+ } else {
+ mWindow.mContextMenu.clearAll();
+ }
+
+ final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
+ getContext(), originalView, x, y);
+ if (helper != null) {
+ helper.setCallback(mWindow.mContextMenuCallback);
+ } else if (mWindow.mContextMenuPopupHelper != null) {
+ // No menu to show, but if we have a menu currently showing it just became blank.
+ // Close it.
+ mWindow.mContextMenuPopupHelper.dismiss();
+ }
+ mWindow.mContextMenuPopupHelper = helper;
+ return helper != null;
+ }
+
+ @Override
+ public ActionMode startActionModeForChild(View originalView,
+ ActionMode.Callback callback) {
+ return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
+ }
+
+ @Override
+ public ActionMode startActionModeForChild(
+ View child, ActionMode.Callback callback, int type) {
+ return startActionMode(child, callback, type);
+ }
+
+ @Override
+ public ActionMode startActionMode(ActionMode.Callback callback) {
+ return startActionMode(callback, ActionMode.TYPE_PRIMARY);
+ }
+
+ @Override
+ public ActionMode startActionMode(ActionMode.Callback callback, int type) {
+ return startActionMode(this, callback, type);
+ }
+
+ private ActionMode startActionMode(
+ View originatingView, ActionMode.Callback callback, int type) {
+ ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
+ ActionMode mode = null;
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
+ try {
+ mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
+ } catch (AbstractMethodError ame) {
+ // Older apps might not implement the typed version of this method.
+ if (type == ActionMode.TYPE_PRIMARY) {
+ try {
+ mode = mWindow.getCallback().onWindowStartingActionMode(
+ wrappedCallback);
+ } catch (AbstractMethodError ame2) {
+ // Older apps might not implement this callback method at all.
+ }
+ }
+ }
+ }
+ if (mode != null) {
+ if (mode.getType() == ActionMode.TYPE_PRIMARY) {
+ cleanupPrimaryActionMode();
+ mPrimaryActionMode = mode;
+ } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
+ if (mFloatingActionMode != null) {
+ mFloatingActionMode.finish();
+ }
+ mFloatingActionMode = mode;
+ }
+ } else {
+ mode = createActionMode(type, wrappedCallback, originatingView);
+ if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
+ setHandledActionMode(mode);
+ } else {
+ mode = null;
+ }
+ }
+ if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
+ try {
+ mWindow.getCallback().onActionModeStarted(mode);
+ } catch (AbstractMethodError ame) {
+ // Older apps might not implement this callback method.
+ }
+ }
+ return mode;
+ }
+
+ private void cleanupPrimaryActionMode() {
+ if (mPrimaryActionMode != null) {
+ mPrimaryActionMode.finish();
+ mPrimaryActionMode = null;
+ }
+ if (mPrimaryActionModeView != null) {
+ mPrimaryActionModeView.killMode();
+ }
+ }
+
+ private void cleanupFloatingActionModeViews() {
+ if (mFloatingToolbar != null) {
+ mFloatingToolbar.dismiss();
+ mFloatingToolbar = null;
+ }
+ if (mFloatingActionModeOriginatingView != null) {
+ if (mFloatingToolbarPreDrawListener != null) {
+ mFloatingActionModeOriginatingView.getViewTreeObserver()
+ .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
+ mFloatingToolbarPreDrawListener = null;
+ }
+ mFloatingActionModeOriginatingView = null;
+ }
+ }
+
+ void startChanging() {
+ mChanging = true;
+ }
+
+ void finishChanging() {
+ mChanging = false;
+ drawableChanged();
+ }
+
+ public void setWindowBackground(Drawable drawable) {
+ if (getBackground() != drawable) {
+ setBackgroundDrawable(drawable);
+ if (drawable != null) {
+ drawable.getPadding(mBackgroundPadding);
+ } else {
+ mBackgroundPadding.setEmpty();
+ }
+ drawableChanged();
+ }
+ }
+
+ public void setWindowFrame(Drawable drawable) {
+ if (getForeground() != drawable) {
+ setForeground(drawable);
+ if (drawable != null) {
+ drawable.getPadding(mFramePadding);
+ } else {
+ mFramePadding.setEmpty();
+ }
+ drawableChanged();
+ }
+ }
+
+ @Override
+ public void onWindowSystemUiVisibilityChanged(int visible) {
+ updateColorViews(null /* insets */, true /* animate */);
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mFrameOffsets.set(insets.getSystemWindowInsets());
+ insets = updateColorViews(insets, true /* animate */);
+ insets = updateStatusGuard(insets);
+ updateNavigationGuard(insets);
+ if (getForeground() != null) {
+ drawableChanged();
+ }
+ return insets;
+ }
+
+ @Override
+ public boolean isTransitionGroup() {
+ return false;
+ }
+
+ WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
+ WindowManager.LayoutParams attrs = mWindow.getAttributes();
+ int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
+
+ if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
+ boolean disallowAnimate = !isLaidOut();
+ disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
+ & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ mLastWindowFlags = attrs.flags;
+
+ if (insets != null) {
+ mLastTopInset = Math.min(insets.getStableInsetTop(),
+ insets.getSystemWindowInsetTop());
+ mLastBottomInset = Math.min(insets.getStableInsetBottom(),
+ insets.getSystemWindowInsetBottom());
+ mLastRightInset = Math.min(insets.getStableInsetRight(),
+ insets.getSystemWindowInsetRight());
+
+ // Don't animate if the presence of stable insets has changed, because that
+ // indicates that the window was either just added and received them for the
+ // first time, or the window size or position has changed.
+ boolean hasTopStableInset = insets.getStableInsetTop() != 0;
+ disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
+ mLastHasTopStableInset = hasTopStableInset;
+
+ boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
+ disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
+ mLastHasBottomStableInset = hasBottomStableInset;
+
+ boolean hasRightStableInset = insets.getStableInsetRight() != 0;
+ disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
+ mLastHasRightStableInset = hasRightStableInset;
+ }
+
+ boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
+ int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
+ updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
+ mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
+ 0 /* rightInset */, animate && !disallowAnimate);
+
+ boolean statusBarNeedsRightInset = navBarToRightEdge
+ && mNavigationColorViewState.present;
+ int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
+ updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
+ mLastTopInset, false /* matchVertical */, statusBarRightInset,
+ animate && !disallowAnimate);
+ }
+
+ // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
+ // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
+ // explicitly asked for it.
+
+ boolean consumingNavBar =
+ (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
+ && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+
+ int consumedRight = consumingNavBar ? mLastRightInset : 0;
+ int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
+
+ if (mContentRoot != null
+ && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
+ MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
+ if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
+ lp.rightMargin = consumedRight;
+ lp.bottomMargin = consumedBottom;
+ mContentRoot.setLayoutParams(lp);
+
+ if (insets == null) {
+ // The insets have changed, but we're not currently in the process
+ // of dispatching them.
+ requestApplyInsets();
+ }
+ }
+ if (insets != null) {
+ insets = insets.replaceSystemWindowInsets(
+ insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight() - consumedRight,
+ insets.getSystemWindowInsetBottom() - consumedBottom);
+ }
+ }
+
+ if (insets != null) {
+ insets = insets.consumeStableInsets();
+ }
+ return insets;
+ }
+
+ /**
+ * Update a color view
+ *
+ * @param state the color view to update.
+ * @param sysUiVis the current systemUiVisibility to apply.
+ * @param color the current color to apply.
+ * @param size the current size in the non-parent-matching dimension.
+ * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
+ * horizontal edge,
+ * @param rightMargin rightMargin for the color view.
+ * @param animate if true, the change will be animated.
+ */
+ private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
+ int size, boolean verticalBar, int rightMargin, boolean animate) {
+ state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
+ && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ boolean show = state.present
+ && (color & Color.BLACK) != 0
+ && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
+
+ boolean visibilityChanged = false;
+ View view = state.view;
+
+ int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
+ int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
+ int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
+
+ if (view == null) {
+ if (show) {
+ state.view = view = new View(mContext);
+ view.setBackgroundColor(color);
+ view.setTransitionName(state.transitionName);
+ view.setId(state.id);
+ visibilityChanged = true;
+ view.setVisibility(INVISIBLE);
+ state.targetVisibility = VISIBLE;
+
+ LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
+ resolvedGravity);
+ lp.rightMargin = rightMargin;
+ addView(view, lp);
+ updateColorViewTranslations();
+ }
+ } else {
+ int vis = show ? VISIBLE : INVISIBLE;
+ visibilityChanged = state.targetVisibility != vis;
+ state.targetVisibility = vis;
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ if (lp.height != resolvedHeight || lp.width != resolvedWidth
+ || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+ lp.height = resolvedHeight;
+ lp.width = resolvedWidth;
+ lp.gravity = resolvedGravity;
+ lp.rightMargin = rightMargin;
+ view.setLayoutParams(lp);
+ }
+ if (show) {
+ view.setBackgroundColor(color);
+ }
+ }
+ if (visibilityChanged) {
+ view.animate().cancel();
+ if (animate) {
+ if (show) {
+ if (view.getVisibility() != VISIBLE) {
+ view.setVisibility(VISIBLE);
+ view.setAlpha(0.0f);
+ }
+ view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
+ setDuration(mBarEnterExitDuration);
+ } else {
+ view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
+ .setDuration(mBarEnterExitDuration)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ state.view.setAlpha(1.0f);
+ state.view.setVisibility(INVISIBLE);
+ }
+ });
+ }
+ } else {
+ view.setAlpha(1.0f);
+ view.setVisibility(show ? VISIBLE : INVISIBLE);
+ }
+ }
+ }
+
+ private void updateColorViewTranslations() {
+ // Put the color views back in place when they get moved off the screen
+ // due to the the ViewRootImpl panning.
+ int rootScrollY = mRootScrollY;
+ if (mStatusColorViewState.view != null) {
+ mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
+ }
+ if (mNavigationColorViewState.view != null) {
+ mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
+ }
+ }
+
+ private WindowInsets updateStatusGuard(WindowInsets insets) {
+ boolean showStatusGuard = false;
+ // Show the status guard when the non-overlay contextual action bar is showing
+ if (mPrimaryActionModeView != null) {
+ if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
+ // Insets are magic!
+ final MarginLayoutParams mlp = (MarginLayoutParams)
+ mPrimaryActionModeView.getLayoutParams();
+ boolean mlpChanged = false;
+ if (mPrimaryActionModeView.isShown()) {
+ if (mTempRect == null) {
+ mTempRect = new Rect();
+ }
+ final Rect rect = mTempRect;
+
+ // If the parent doesn't consume the insets, manually
+ // apply the default system window insets.
+ mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
+ final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
+ if (mlp.topMargin != newMargin) {
+ mlpChanged = true;
+ mlp.topMargin = insets.getSystemWindowInsetTop();
+
+ if (mStatusGuard == null) {
+ mStatusGuard = new View(mContext);
+ mStatusGuard.setBackgroundColor(mContext.getColor(
+ R.color.input_method_navigation_guard));
+ addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
+ new LayoutParams(LayoutParams.MATCH_PARENT,
+ mlp.topMargin, Gravity.START | Gravity.TOP));
+ } else {
+ final LayoutParams lp = (LayoutParams)
+ mStatusGuard.getLayoutParams();
+ if (lp.height != mlp.topMargin) {
+ lp.height = mlp.topMargin;
+ mStatusGuard.setLayoutParams(lp);
+ }
+ }
+ }
+
+ // The action mode's theme may differ from the app, so
+ // always show the status guard above it if we have one.
+ showStatusGuard = mStatusGuard != null;
+
+ // We only need to consume the insets if the action
+ // mode is overlaid on the app content (e.g. it's
+ // sitting in a FrameLayout, see
+ // screen_simple_overlay_action_mode.xml).
+ final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
+ & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
+ insets = insets.consumeSystemWindowInsets(
+ false, nonOverlay && showStatusGuard /* top */, false, false);
+ } else {
+ // reset top margin
+ if (mlp.topMargin != 0) {
+ mlpChanged = true;
+ mlp.topMargin = 0;
+ }
+ }
+ if (mlpChanged) {
+ mPrimaryActionModeView.setLayoutParams(mlp);
+ }
+ }
+ }
+ if (mStatusGuard != null) {
+ mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
+ }
+ return insets;
+ }
+
+ private void updateNavigationGuard(WindowInsets insets) {
+ // IMEs lay out below the nav bar, but the content view must not (for back compat)
+ if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+ // prevent the content view from including the nav bar height
+ if (mWindow.mContentParent != null) {
+ if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
+ MarginLayoutParams mlp =
+ (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
+ mlp.bottomMargin = insets.getSystemWindowInsetBottom();
+ mWindow.mContentParent.setLayoutParams(mlp);
+ }
+ }
+ // position the navigation guard view, creating it if necessary
+ if (mNavigationGuard == null) {
+ mNavigationGuard = new View(mContext);
+ mNavigationGuard.setBackgroundColor(mContext.getColor(
+ R.color.input_method_navigation_guard));
+ addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
+ new LayoutParams(LayoutParams.MATCH_PARENT,
+ insets.getSystemWindowInsetBottom(),
+ Gravity.START | Gravity.BOTTOM));
+ } else {
+ LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
+ lp.height = insets.getSystemWindowInsetBottom();
+ mNavigationGuard.setLayoutParams(lp);
+ }
+ }
+ }
+
+ private void drawableChanged() {
+ if (mChanging) {
+ return;
+ }
+
+ setPadding(mFramePadding.left + mBackgroundPadding.left,
+ mFramePadding.top + mBackgroundPadding.top,
+ mFramePadding.right + mBackgroundPadding.right,
+ mFramePadding.bottom + mBackgroundPadding.bottom);
+ requestLayout();
+ invalidate();
+
+ int opacity = PixelFormat.OPAQUE;
+ if (ActivityManager.StackId.hasWindowShadow(mStackId)) {
+ // If the window has a shadow, it must be translucent.
+ opacity = PixelFormat.TRANSLUCENT;
+ } else{
+ // Note: If there is no background, we will assume opaque. The
+ // common case seems to be that an application sets there to be
+ // no background so it can draw everything itself. For that,
+ // we would like to assume OPAQUE and let the app force it to
+ // the slower TRANSLUCENT mode if that is really what it wants.
+ Drawable bg = getBackground();
+ Drawable fg = getForeground();
+ if (bg != null) {
+ if (fg == null) {
+ opacity = bg.getOpacity();
+ } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
+ && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
+ // If the frame padding is zero, then we can be opaque
+ // if either the frame -or- the background is opaque.
+ int fop = fg.getOpacity();
+ int bop = bg.getOpacity();
+ if (false)
+ Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
+ if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
+ opacity = PixelFormat.OPAQUE;
+ } else if (fop == PixelFormat.UNKNOWN) {
+ opacity = bop;
+ } else if (bop == PixelFormat.UNKNOWN) {
+ opacity = fop;
+ } else {
+ opacity = Drawable.resolveOpacity(fop, bop);
+ }
+ } else {
+ // For now we have to assume translucent if there is a
+ // frame with padding... there is no way to tell if the
+ // frame and background together will draw all pixels.
+ if (false)
+ Log.v(TAG, "Padding: " + mFramePadding);
+ opacity = PixelFormat.TRANSLUCENT;
+ }
+ }
+ if (false)
+ Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
+ }
+
+ if (false)
+ Log.v(TAG, "Selected default opacity: " + opacity);
+
+ mDefaultOpacity = opacity;
+ if (mFeatureId < 0) {
+ mWindow.setDefaultWindowFormat(opacity);
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ // If the user is chording a menu shortcut, release the chord since
+ // this window lost focus
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
+ && mWindow.mPanelChordingKey != 0) {
+ mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ final Window.Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
+ cb.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ if (mPrimaryActionMode != null) {
+ mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
+ }
+ if (mFloatingActionMode != null) {
+ mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
+ }
+
+ updateElevation();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ final Window.Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
+ cb.onAttachedToWindow();
+ }
+
+ if (mFeatureId == -1) {
+ /*
+ * The main window has been attached, try to restore any panels
+ * that may have been open before. This is called in cases where
+ * an activity is being killed for configuration change and the
+ * menu was open. When the activity is recreated, the menu
+ * should be shown again.
+ */
+ mWindow.openPanelsAfterRestore();
+ }
+
+ if (!mWindowResizeCallbacksAdded) {
+ // If there is no window callback installed there was no window set before. Set it now.
+ // Note that our ViewRootImpl object will not change.
+ getViewRootImpl().addWindowCallbacks(this);
+ mWindowResizeCallbacksAdded = true;
+ } else if (mBackdropFrameRenderer != null) {
+ // We are resizing and this call happened due to a configuration change. Tell the
+ // renderer about it.
+ mBackdropFrameRenderer.onConfigurationChange();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ final Window.Callback cb = mWindow.getCallback();
+ if (cb != null && mFeatureId < 0) {
+ cb.onDetachedFromWindow();
+ }
+
+ if (mWindow.mDecorContentParent != null) {
+ mWindow.mDecorContentParent.dismissPopups();
+ }
+
+ if (mPrimaryActionModePopup != null) {
+ removeCallbacks(mShowPrimaryActionModePopup);
+ if (mPrimaryActionModePopup.isShowing()) {
+ mPrimaryActionModePopup.dismiss();
+ }
+ mPrimaryActionModePopup = null;
+ }
+ if (mFloatingToolbar != null) {
+ mFloatingToolbar.dismiss();
+ mFloatingToolbar = null;
+ }
+
+ PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
+ if (st != null && st.menu != null && mFeatureId < 0) {
+ st.menu.close();
+ }
+
+ if (mWindowResizeCallbacksAdded) {
+ getViewRootImpl().removeWindowCallbacks(this);
+ mWindowResizeCallbacksAdded = false;
+ }
+ }
+
+ @Override
+ public void onCloseSystemDialogs(String reason) {
+ if (mFeatureId >= 0) {
+ mWindow.closeAllPanels();
+ }
+ }
+
+ public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
+ return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
+ }
+
+ public InputQueue.Callback willYouTakeTheInputQueue() {
+ return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
+ }
+
+ public void setSurfaceType(int type) {
+ mWindow.setType(type);
+ }
+
+ public void setSurfaceFormat(int format) {
+ mWindow.setFormat(format);
+ }
+
+ public void setSurfaceKeepScreenOn(boolean keepOn) {
+ if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ @Override
+ public void onRootViewScrollYChanged(int rootScrollY) {
+ mRootScrollY = rootScrollY;
+ updateColorViewTranslations();
+ }
+
+ private ActionMode createActionMode(
+ int type, ActionMode.Callback2 callback, View originatingView) {
+ switch (type) {
+ case ActionMode.TYPE_PRIMARY:
+ default:
+ return createStandaloneActionMode(callback);
+ case ActionMode.TYPE_FLOATING:
+ return createFloatingActionMode(originatingView, callback);
+ }
+ }
+
+ private void setHandledActionMode(ActionMode mode) {
+ if (mode.getType() == ActionMode.TYPE_PRIMARY) {
+ setHandledPrimaryActionMode(mode);
+ } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
+ setHandledFloatingActionMode(mode);
+ }
+ }
+
+ private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
+ endOnGoingFadeAnimation();
+ cleanupPrimaryActionMode();
+ if (mPrimaryActionModeView == null) {
+ if (mWindow.isFloating()) {
+ // Use the action bar theme.
+ final TypedValue outValue = new TypedValue();
+ final Resources.Theme baseTheme = mContext.getTheme();
+ baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
+
+ final Context actionBarContext;
+ if (outValue.resourceId != 0) {
+ final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
+ actionBarTheme.setTo(baseTheme);
+ actionBarTheme.applyStyle(outValue.resourceId, true);
+
+ actionBarContext = new ContextThemeWrapper(mContext, 0);
+ actionBarContext.getTheme().setTo(actionBarTheme);
+ } else {
+ actionBarContext = mContext;
+ }
+
+ mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
+ mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
+ R.attr.actionModePopupWindowStyle);
+ mPrimaryActionModePopup.setWindowLayoutType(
+ WindowManager.LayoutParams.TYPE_APPLICATION);
+ mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
+ mPrimaryActionModePopup.setWidth(MATCH_PARENT);
+
+ actionBarContext.getTheme().resolveAttribute(
+ R.attr.actionBarSize, outValue, true);
+ final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
+ actionBarContext.getResources().getDisplayMetrics());
+ mPrimaryActionModeView.setContentHeight(height);
+ mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
+ mShowPrimaryActionModePopup = new Runnable() {
+ public void run() {
+ mPrimaryActionModePopup.showAtLocation(
+ mPrimaryActionModeView.getApplicationWindowToken(),
+ Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
+ endOnGoingFadeAnimation();
+ mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
+ 0f, 1f);
+ mFadeAnim.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mPrimaryActionModeView.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPrimaryActionModeView.setAlpha(1f);
+ mFadeAnim = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ mFadeAnim.start();
+ }
+ };
+ } else {
+ ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
+ if (stub != null) {
+ mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
+ }
+ }
+ }
+ if (mPrimaryActionModeView != null) {
+ mPrimaryActionModeView.killMode();
+ ActionMode mode = new StandaloneActionMode(
+ mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
+ callback, mPrimaryActionModePopup == null);
+ return mode;
+ }
+ return null;
+ }
+
+ private void endOnGoingFadeAnimation() {
+ if (mFadeAnim != null) {
+ mFadeAnim.end();
+ }
+ }
+
+ private void setHandledPrimaryActionMode(ActionMode mode) {
+ endOnGoingFadeAnimation();
+ mPrimaryActionMode = mode;
+ mPrimaryActionMode.invalidate();
+ mPrimaryActionModeView.initForMode(mPrimaryActionMode);
+ if (mPrimaryActionModePopup != null) {
+ post(mShowPrimaryActionModePopup);
+ } else {
+ mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
+ mFadeAnim.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mPrimaryActionModeView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPrimaryActionModeView.setAlpha(1f);
+ mFadeAnim = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ mFadeAnim.start();
+ }
+ mPrimaryActionModeView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ private ActionMode createFloatingActionMode(
+ View originatingView, ActionMode.Callback2 callback) {
+ if (mFloatingActionMode != null) {
+ mFloatingActionMode.finish();
+ }
+ cleanupFloatingActionModeViews();
+ final FloatingActionMode mode =
+ new FloatingActionMode(mContext, callback, originatingView);
+ mFloatingActionModeOriginatingView = originatingView;
+ mFloatingToolbarPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mode.updateViewLocationInWindow();
+ return true;
+ }
+ };
+ return mode;
+ }
+
+ private void setHandledFloatingActionMode(ActionMode mode) {
+ mFloatingActionMode = mode;
+ mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
+ ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
+ mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
+ mFloatingActionModeOriginatingView.getViewTreeObserver()
+ .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
+ }
+
+ /**
+ * Informs the decor if the caption is attached and visible.
+ * @param attachedAndVisible true when the decor is visible.
+ * Note that this will even be called if there is no caption.
+ **/
+ void enableCaption(boolean attachedAndVisible) {
+ if (mHasCaption != attachedAndVisible) {
+ mHasCaption = attachedAndVisible;
+ if (getForeground() != null) {
+ drawableChanged();
+ }
+ }
+ }
+
+ void setWindow(PhoneWindow phoneWindow) {
+ mWindow = phoneWindow;
+ Context context = getContext();
+ if (context instanceof DecorContext) {
+ DecorContext decorContext = (DecorContext) context;
+ decorContext.setPhoneWindow(mWindow);
+ }
+ }
+
+ void onConfigurationChanged() {
+ int workspaceId = getStackId();
+ if (mDecorCaptionView != null) {
+ if (mStackId != workspaceId) {
+ mStackId = workspaceId;
+ // We might have to change the kind of surface before we do anything else.
+ mDecorCaptionView.onConfigurationChanged(
+ ActivityManager.StackId.hasWindowDecor(mStackId));
+ enableCaption(ActivityManager.StackId.hasWindowDecor(workspaceId));
+ }
+ }
+ initializeElevation();
+ }
+
+ View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
+ mStackId = getStackId();
+
+ mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+ mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+ mCaptionBackgroundDrawable =
+ getContext().getDrawable(R.drawable.decor_caption_title_focused);
+
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.onResourcesLoaded(
+ this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);
+ }
+
+ mDecorCaptionView = createDecorCaptionView(inflater);
+ final View root = inflater.inflate(layoutResource, null);
+ if (mDecorCaptionView != null) {
+ if (mDecorCaptionView.getParent() == null) {
+ addView(mDecorCaptionView,
+ new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
+ mDecorCaptionView.addView(root,
+ new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
+ } else {
+ addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
+ mContentRoot = (ViewGroup) root;
+ initializeElevation();
+ return root;
+ }
+
+ // Free floating overlapping windows require a caption.
+ private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
+ DecorCaptionView DecorCaptionView = null;
+ for (int i = getChildCount() - 1; i >= 0 && DecorCaptionView == null; i--) {
+ View view = getChildAt(i);
+ if (view instanceof DecorCaptionView) {
+ // The decor was most likely saved from a relaunch - so reuse it.
+ DecorCaptionView = (DecorCaptionView) view;
+ removeViewAt(i);
+ }
+ }
+ final WindowManager.LayoutParams attrs = mWindow.getAttributes();
+ final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
+ attrs.type == TYPE_APPLICATION;
+ // Only a non floating application window on one of the allowed workspaces can get a caption
+ if (!mWindow.isFloating() && isApplication
+ && ActivityManager.StackId.hasWindowDecor(mStackId)) {
+ // Dependent on the brightness of the used title we either use the
+ // dark or the light button frame.
+ if (DecorCaptionView == null) {
+ Context context = getContext();
+ TypedValue value = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+ inflater = inflater.from(context);
+ if (Color.luminance(value.data) < 0.5) {
+ DecorCaptionView = (DecorCaptionView) inflater.inflate(
+ R.layout.decor_caption_dark, null);
+ } else {
+ DecorCaptionView = (DecorCaptionView) inflater.inflate(
+ R.layout.decor_caption_light, null);
+ }
+ }
+ DecorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
+ } else {
+ DecorCaptionView = null;
+ }
+
+ // Tell the decor if it has a visible caption.
+ enableCaption(DecorCaptionView != null);
+ return DecorCaptionView;
+ }
+
+ /**
+ * Returns the color used to fill areas the app has not rendered content to yet when the
+ * user is resizing the window of an activity in multi-window mode.
+ */
+ private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
+ final Context context = getContext();
+
+ if (backgroundRes != 0) {
+ final Drawable drawable = context.getDrawable(backgroundRes);
+ if (drawable != null) {
+ return drawable;
+ }
+ }
+
+ if (backgroundFallbackRes != 0) {
+ final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
+ if (fallbackDrawable != null) {
+ return fallbackDrawable;
+ }
+ }
+
+ // We shouldn't really get here as the background fallback should be always available since
+ // it is defaulted by the system.
+ Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + mWindow);
+ return null;
+ }
+
+ /**
+ * Returns the Id of the stack which contains this window.
+ * Note that if no stack can be determined - which usually means that it was not
+ * created for an activity - the fullscreen stack ID will be returned.
+ * @return Returns the stack id which contains this window.
+ **/
+ private int getStackId() {
+ int workspaceId = INVALID_STACK_ID;
+ final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
+ if (callback != null) {
+ try {
+ workspaceId = callback.getWindowStackId();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
+ }
+ }
+ if (workspaceId == INVALID_STACK_ID) {
+ return FULLSCREEN_WORKSPACE_STACK_ID;
+ }
+ return workspaceId;
+ }
+
+ void clearContentView() {
+ if (mDecorCaptionView != null) {
+ mDecorCaptionView.removeContentView();
+ } else {
+ // This window doesn't have caption, so we need to just remove the
+ // children of the decor view.
+ removeAllViews();
+ }
+ }
+
+ @Override
+ public void onWindowSizeIsChanging(Rect newBounds) {
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.setTargetRect(newBounds);
+ }
+ }
+
+ @Override
+ public void onWindowDragResizeStart(Rect initialBounds) {
+ if (mWindow.isDestroyed()) {
+ // If the owner's window is gone, we should not be able to come here anymore.
+ releaseThreadedRenderer();
+ return;
+ }
+ if (mBackdropFrameRenderer != null) {
+ return;
+ }
+ final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
+ if (renderer != null) {
+ mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
+ initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);
+
+ // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
+ // If we want to get the shadow shown while resizing, we would need to elevate a new
+ // element which owns the caption and has the elevation.
+ updateElevation();
+ }
+ }
+
+ @Override
+ public void onWindowDragResizeEnd() {
+ releaseThreadedRenderer();
+ }
+
+ @Override
+ public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+ if (mBackdropFrameRenderer == null) {
+ return false;
+ }
+ return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
+ }
+
+ @Override
+ public void onRequestDraw(boolean reportNextDraw) {
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
+ } else if (reportNextDraw) {
+ // If render thread is gone, just report immediately.
+ if (isAttachedToWindow()) {
+ getViewRootImpl().reportDrawFinish();
+ }
+ }
+ }
+
+ /** Release the renderer thread which is usually done when the user stops resizing. */
+ private void releaseThreadedRenderer() {
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.releaseRenderer();
+ mBackdropFrameRenderer = null;
+ // Bring the shadow back.
+ updateElevation();
+ }
+ }
+
+ /**
+ * The elevation gets set for the first time and the framework needs to be informed that
+ * the surface layer gets created with the shadow size in mind.
+ */
+ private void initializeElevation() {
+ // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
+ mAllowUpdateElevation = false;
+ updateElevation();
+ }
+
+ private void updateElevation() {
+ float elevation = 0;
+ final boolean wasAdjustedForStack = mElevationAdjustedForStack;
+ // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
+ // since the shadow is bound to the content size and not the target size.
+ if (ActivityManager.StackId.hasWindowShadow(mStackId)
+ && mBackdropFrameRenderer == null) {
+ elevation = hasWindowFocus() ?
+ DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
+ // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
+ if (!mAllowUpdateElevation) {
+ elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
+ }
+ // Convert the DP elevation into physical pixels.
+ elevation = dipToPx(elevation);
+ mElevationAdjustedForStack = true;
+ } else {
+ mElevationAdjustedForStack = false;
+ }
+
+ // Don't change the elevation if we didn't previously adjust it for the stack it was in
+ // or it didn't change.
+ if ((wasAdjustedForStack || mElevationAdjustedForStack)
+ && getElevation() != elevation) {
+ mWindow.setElevation(elevation);
+ }
+ }
+
+ boolean isShowingCaption() {
+ return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
+ }
+
+ int getCaptionHeight() {
+ return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
+ }
+
+ /**
+ * Converts a DIP measure into physical pixels.
+ * @param dip The dip value.
+ * @return Returns the number of pixels.
+ */
+ private float dipToPx(float dip) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
+ getResources().getDisplayMetrics());
+ }
+
+ private static class ColorViewState {
+ View view = null;
+ int targetVisibility = View.INVISIBLE;
+ boolean present = false;
+
+ final int id;
+ final int systemUiHideFlag;
+ final int translucentFlag;
+ final int verticalGravity;
+ final int horizontalGravity;
+ final String transitionName;
+ final int hideWindowFlag;
+
+ ColorViewState(int systemUiHideFlag,
+ int translucentFlag, int verticalGravity, int horizontalGravity,
+ String transitionName, int id, int hideWindowFlag) {
+ this.id = id;
+ this.systemUiHideFlag = systemUiHideFlag;
+ this.translucentFlag = translucentFlag;
+ this.verticalGravity = verticalGravity;
+ this.horizontalGravity = horizontalGravity;
+ this.transitionName = transitionName;
+ this.hideWindowFlag = hideWindowFlag;
+ }
+ }
+
+ /**
+ * Clears out internal references when the action mode is destroyed.
+ */
+ private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
+ private final ActionMode.Callback mWrapped;
+
+ public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
+ mWrapped = wrapped;
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return mWrapped.onCreateActionMode(mode, menu);
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ requestFitSystemWindows();
+ return mWrapped.onPrepareActionMode(mode, menu);
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mWrapped.onActionItemClicked(mode, item);
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ mWrapped.onDestroyActionMode(mode);
+ final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.M;
+ final boolean isPrimary;
+ final boolean isFloating;
+ if (isMncApp) {
+ isPrimary = mode == mPrimaryActionMode;
+ isFloating = mode == mFloatingActionMode;
+ if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
+ Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
+ + mode + " was not the current primary action mode! Expected "
+ + mPrimaryActionMode);
+ }
+ if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
+ Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
+ + mode + " was not the current floating action mode! Expected "
+ + mFloatingActionMode);
+ }
+ } else {
+ isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
+ isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
+ }
+ if (isPrimary) {
+ if (mPrimaryActionModePopup != null) {
+ removeCallbacks(mShowPrimaryActionModePopup);
+ }
+ if (mPrimaryActionModeView != null) {
+ endOnGoingFadeAnimation();
+ mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
+ 1f, 0f);
+ mFadeAnim.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPrimaryActionModeView.setVisibility(GONE);
+ if (mPrimaryActionModePopup != null) {
+ mPrimaryActionModePopup.dismiss();
+ }
+ mPrimaryActionModeView.removeAllViews();
+ mFadeAnim = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+ mFadeAnim.start();
+ }
+
+ mPrimaryActionMode = null;
+ } else if (isFloating) {
+ cleanupFloatingActionModeViews();
+ mFloatingActionMode = null;
+ }
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
+ try {
+ mWindow.getCallback().onActionModeFinished(mode);
+ } catch (AbstractMethodError ame) {
+ // Older apps might not implement this callback method.
+ }
+ }
+ requestFitSystemWindows();
+ }
+
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ if (mWrapped instanceof ActionMode.Callback2) {
+ ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
+ } else {
+ super.onGetContentRect(mode, view, outRect);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 83f810f..6e7e5cf 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -16,24 +16,14 @@
package com.android.internal.policy;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.getMode;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.*;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
import android.app.SearchManager;
-import android.os.Build;
import android.os.UserHandle;
-import android.view.ActionMode;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
@@ -55,15 +45,9 @@
import android.view.ViewManager;
import android.view.ViewParent;
import android.view.ViewRootImpl;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.internal.R;
-import com.android.internal.view.FloatingActionMode;
-import com.android.internal.view.RootViewSurfaceTaker;
-import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.IconMenuPresenter;
import com.android.internal.view.menu.ListMenuPresenter;
@@ -72,11 +56,7 @@
import com.android.internal.view.menu.MenuPopupHelper;
import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.MenuView;
-import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorContentParent;
-import com.android.internal.widget.FloatingToolbar;
-import com.android.internal.widget.NonClientDecorView;
import com.android.internal.widget.SwipeDismissLayout;
import android.app.ActivityManager;
@@ -87,10 +67,7 @@
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -108,22 +85,17 @@
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ImageView;
-import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -142,8 +114,6 @@
private final static String TAG = "PhoneWindow";
- private final static boolean SWEEP_OPEN_MENU = false;
-
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
@@ -174,32 +144,21 @@
// view is requested, so we need to force the recreating without introducing an infinite loop.
private boolean mForceDecorInstall = false;
- // This is the non client decor view for the window, containing the caption and window control
- // buttons. The visibility of this decor depends on the workspace and the window type.
- // If the window type does not require such a view, this member might be null.
- NonClientDecorView mNonClientDecorView;
-
- // The non client decor needs to adapt to the used workspace. Since querying and changing the
- // workspace is expensive, this is the workspace value the window is currently set up for.
- int mWorkspaceId;
-
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
- private ViewGroup mContentParent;
-
- private ViewGroup mContentRoot;
+ ViewGroup mContentParent;
Callback2 mTakeSurfaceCallback;
InputQueue.Callback mTakeInputQueueCallback;
- private boolean mIsFloating;
+ boolean mIsFloating;
private LayoutInflater mLayoutInflater;
private TextView mTitleView;
- private DecorContentParent mDecorContentParent;
+ DecorContentParent mDecorContentParent;
private ActionMenuPresenterCallback mActionMenuPresenterCallback;
private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
@@ -231,13 +190,13 @@
* multiple panels). Shortcuts will go to this panel. It gets set in
* {@link #preparePanel} and cleared in {@link #closePanel}.
*/
- private PanelFeatureState mPreparedPanel;
+ PanelFeatureState mPreparedPanel;
/**
* The keycode that is currently held down (as a modifier) for chording. If
* this is 0, there is no key held down.
*/
- private int mPanelChordingKey;
+ int mPanelChordingKey;
private ImageView mLeftIconView;
@@ -247,12 +206,12 @@
private ProgressBar mHorizontalProgressBar;
- private int mBackgroundResource = 0;
- private int mBackgroundFallbackResource = 0;
+ int mBackgroundResource = 0;
+ int mBackgroundFallbackResource = 0;
private Drawable mBackgroundDrawable;
- private boolean mLoadEleveation = true;
+ private boolean mLoadElevation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
@@ -261,8 +220,8 @@
private int mFrameResource = 0;
private int mTextColor = 0;
- private int mStatusBarColor = 0;
- private int mNavigationBarColor = 0;
+ int mStatusBarColor = 0;
+ int mNavigationBarColor = 0;
private boolean mForcedStatusBarColor = false;
private boolean mForcedNavigationBarColor = false;
@@ -272,9 +231,9 @@
private boolean mAlwaysReadCloseOnTouchAttr = false;
- private ContextMenuBuilder mContextMenu;
- private MenuDialogHelper mContextMenuHelper;
- private MenuPopupHelper mContextMenuPopupHelper;
+ ContextMenuBuilder mContextMenu;
+ MenuDialogHelper mContextMenuHelper;
+ MenuPopupHelper mContextMenuPopupHelper;
private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
@@ -312,9 +271,6 @@
private long mBackgroundFadeDurationMillis = -1;
private Boolean mSharedElementsUseOverlay;
- private Rect mTempRect;
- private Rect mOutsets = new Rect();
-
private boolean mIsStartingWindow;
private int mTheme = -1;
@@ -335,7 +291,7 @@
if (preservedWindow != null) {
mDecor = (DecorView) preservedWindow.getDecorView();
mElevation = preservedWindow.getElevation();
- mLoadEleveation = false;
+ mLoadElevation = false;
mForceDecorInstall = true;
// If we're preserving window, carry over the app token from the preserved
// window, as we'll be skipping the addView in handleResumeActivity(), and
@@ -508,15 +464,10 @@
}
}
+ @Override
public void clearContentView() {
- if (mNonClientDecorView != null) {
- if (mNonClientDecorView.getChildCount() > 1) {
- mNonClientDecorView.removeViewAt(1);
- }
- } else {
- // This window doesn't have non client decor, so we need to just remove the children
- // of the decor view.
- mDecor.removeAllViews();
+ if (mDecor != null) {
+ mDecor.clearContentView();
}
}
@@ -730,15 +681,8 @@
}
}
}
- if (mNonClientDecorView != null) {
- int workspaceId = getWorkspaceId();
- if (mWorkspaceId != workspaceId) {
- mWorkspaceId = workspaceId;
- // We might have to change the kind of surface before we do anything else.
- mNonClientDecorView.phoneWindowUpdated(StackId.hasWindowDecor(mWorkspaceId),
- StackId.hasWindowShadow(mWorkspaceId));
- mDecor.enableNonClientDecor(StackId.hasWindowDecor(workspaceId));
- }
+ if (mDecor != null) {
+ mDecor.onConfigurationChanged();
}
}
@@ -1170,7 +1114,7 @@
return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags);
}
- private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
+ boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
int flags) {
if (event.isSystem() || (st == null)) {
return false;
@@ -2261,7 +2205,7 @@
* called sometime after {@link #restorePanelState} when it is safe to add
* to the window manager.
*/
- private void openPanelsAfterRestore() {
+ void openPanelsAfterRestore() {
PanelFeatureState[] panels = mPanels;
if (panels == null) {
@@ -2332,1530 +2276,6 @@
}
}
- private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
-
- /* package */int mDefaultOpacity = PixelFormat.OPAQUE;
-
- /** The feature ID of the panel, or -1 if this is the application's DecorView */
- private final int mFeatureId;
-
- private final Rect mDrawingBounds = new Rect();
-
- private final Rect mBackgroundPadding = new Rect();
-
- private final Rect mFramePadding = new Rect();
-
- private final Rect mFrameOffsets = new Rect();
-
- // True if a non client area decor exists.
- private boolean mHasNonClientDecor = false;
-
- private boolean mChanging;
-
- private Drawable mMenuBackground;
- private boolean mWatchingForMenu;
- private int mDownY;
-
- private ActionMode mPrimaryActionMode;
- private ActionMode mFloatingActionMode;
- private ActionBarContextView mPrimaryActionModeView;
- private PopupWindow mPrimaryActionModePopup;
- private Runnable mShowPrimaryActionModePopup;
- private OnPreDrawListener mFloatingToolbarPreDrawListener;
- private View mFloatingActionModeOriginatingView;
- private FloatingToolbar mFloatingToolbar;
- private ObjectAnimator mFadeAnim;
-
- // View added at runtime to draw under the status bar area
- private View mStatusGuard;
- // View added at runtime to draw under the navigation bar area
- private View mNavigationGuard;
-
- private final ColorViewState mStatusColorViewState = new ColorViewState(
- SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
- Gravity.TOP,
- Gravity.LEFT,
- STATUS_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.statusBarBackground,
- FLAG_FULLSCREEN);
- private final ColorViewState mNavigationColorViewState = new ColorViewState(
- SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
- Gravity.BOTTOM,
- Gravity.RIGHT,
- NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.navigationBarBackground,
- 0 /* hideWindowFlag */);
-
- private final Interpolator mShowInterpolator;
- private final Interpolator mHideInterpolator;
- private final int mBarEnterExitDuration;
-
- private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
-
- private int mLastTopInset = 0;
- private int mLastBottomInset = 0;
- private int mLastRightInset = 0;
- private boolean mLastHasTopStableInset = false;
- private boolean mLastHasBottomStableInset = false;
- private boolean mLastHasRightStableInset = false;
- private int mLastWindowFlags = 0;
-
- private int mRootScrollY = 0;
-
- private PhoneWindow mWindow;
-
- private DecorView(Context context, int featureId, PhoneWindow window) {
- super(context);
- mFeatureId = featureId;
-
- mShowInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.linear_out_slow_in);
- mHideInterpolator = AnimationUtils.loadInterpolator(context,
- android.R.interpolator.fast_out_linear_in);
-
- mBarEnterExitDuration = context.getResources().getInteger(
- R.integer.dock_enter_exit_duration);
-
- setWindow(window);
- }
-
- public void setBackgroundFallback(int resId) {
- mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
- setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
- }
-
- @Override
- public void onDraw(Canvas c) {
- super.onDraw(c);
- mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- final int keyCode = event.getKeyCode();
- final int action = event.getAction();
- final boolean isDown = action == KeyEvent.ACTION_DOWN;
-
- if (isDown && (event.getRepeatCount() == 0)) {
- // First handle chording of panel key: if a panel key is held
- // but not released, try to execute a shortcut in it.
- if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
- boolean handled = dispatchKeyShortcutEvent(event);
- if (handled) {
- return true;
- }
- }
-
- // If a panel is open, perform a shortcut on it without the
- // chorded panel key
- if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
- if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
- return true;
- }
- }
- }
-
- if (!mWindow.isDestroyed()) {
- final Callback cb = mWindow.getCallback();
- final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
- : super.dispatchKeyEvent(event);
- if (handled) {
- return true;
- }
- }
-
- return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
- : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
- }
-
- @Override
- public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
- // If the panel is already prepared, then perform the shortcut using it.
- boolean handled;
- if (mWindow.mPreparedPanel != null) {
- handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
- Menu.FLAG_PERFORM_NO_CLOSE);
- if (handled) {
- if (mWindow.mPreparedPanel != null) {
- mWindow.mPreparedPanel.isHandled = true;
- }
- return true;
- }
- }
-
- // Shortcut not handled by the panel. Dispatch to the view hierarchy.
- final Callback cb = mWindow.getCallback();
- handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
- ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
- if (handled) {
- return true;
- }
-
- // If the panel is not prepared, then we may be trying to handle a shortcut key
- // combination such as Control+C. Temporarily prepare the panel then mark it
- // unprepared again when finished to ensure that the panel will again be prepared
- // the next time it is shown for real.
- PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (st != null && mWindow.mPreparedPanel == null) {
- mWindow.preparePanel(st, ev);
- handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
- Menu.FLAG_PERFORM_NO_CLOSE);
- st.isPrepared = false;
- if (handled) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- final Callback cb = mWindow.getCallback();
- return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
- ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean dispatchTrackballEvent(MotionEvent ev) {
- final Callback cb = mWindow.getCallback();
- return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
- ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
- }
-
- @Override
- public boolean dispatchGenericMotionEvent(MotionEvent ev) {
- final Callback cb = mWindow.getCallback();
- return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
- ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
- }
-
- public boolean superDispatchKeyEvent(KeyEvent event) {
- // Give priority to closing action modes if applicable.
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
- final int action = event.getAction();
- // Back cancels action modes first.
- if (mPrimaryActionMode != null) {
- if (action == KeyEvent.ACTION_UP) {
- mPrimaryActionMode.finish();
- }
- return true;
- }
- }
-
- return super.dispatchKeyEvent(event);
- }
-
- public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
- return super.dispatchKeyShortcutEvent(event);
- }
-
- public boolean superDispatchTouchEvent(MotionEvent event) {
- return super.dispatchTouchEvent(event);
- }
-
- public boolean superDispatchTrackballEvent(MotionEvent event) {
- return super.dispatchTrackballEvent(event);
- }
-
- public boolean superDispatchGenericMotionEvent(MotionEvent event) {
- return super.dispatchGenericMotionEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return onInterceptTouchEvent(event);
- }
-
- private boolean isOutOfInnerBounds(int x, int y) {
- return x < 0 || y < 0 || x > getWidth() || y > getHeight();
- }
-
- private boolean isOutOfBounds(int x, int y) {
- return x < -5 || y < -5 || x > (getWidth() + 5)
- || y > (getHeight() + 5);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- int action = event.getAction();
- if (mHasNonClientDecor && mWindow.mNonClientDecorView.mVisible) {
- // Don't dispatch ACTION_DOWN to the non client decor if the window is
- // resizable and the event was (starting) outside the window.
- // Window resizing events should be handled by WindowManager.
- // TODO: Investigate how to handle the outside touch in window manager
- // without generating these events.
- // Currently we receive these because we need to enlarge the window's
- // touch region so that the monitor channel receives the events
- // in the outside touch area.
- if (action == MotionEvent.ACTION_DOWN) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (isOutOfInnerBounds(x, y)) {
- return true;
- }
- }
- }
-
- if (mFeatureId >= 0) {
- if (action == MotionEvent.ACTION_DOWN) {
- int x = (int)event.getX();
- int y = (int)event.getY();
- if (isOutOfBounds(x, y)) {
- mWindow.closePanel(mFeatureId);
- return true;
- }
- }
- }
-
- if (!SWEEP_OPEN_MENU) {
- return false;
- }
-
- if (mFeatureId >= 0) {
- if (action == MotionEvent.ACTION_DOWN) {
- Log.i(TAG, "Watchiing!");
- mWatchingForMenu = true;
- mDownY = (int) event.getY();
- return false;
- }
-
- if (!mWatchingForMenu) {
- return false;
- }
-
- int y = (int)event.getY();
- if (action == MotionEvent.ACTION_MOVE) {
- if (y > (mDownY+30)) {
- Log.i(TAG, "Closing!");
- mWindow.closePanel(mFeatureId);
- mWatchingForMenu = false;
- return true;
- }
- } else if (action == MotionEvent.ACTION_UP) {
- mWatchingForMenu = false;
- }
-
- return false;
- }
-
- //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
- // + " (in " + getHeight() + ")");
-
- if (action == MotionEvent.ACTION_DOWN) {
- int y = (int)event.getY();
- if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
- Log.i(TAG, "Watching!");
- mWatchingForMenu = true;
- }
- return false;
- }
-
- if (!mWatchingForMenu) {
- return false;
- }
-
- int y = (int)event.getY();
- if (action == MotionEvent.ACTION_MOVE) {
- if (y < (getHeight()-30)) {
- Log.i(TAG, "Opening!");
- mWindow.openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
- mWatchingForMenu = false;
- return true;
- }
- } else if (action == MotionEvent.ACTION_UP) {
- mWatchingForMenu = false;
- }
-
- return false;
- }
-
- @Override
- public void sendAccessibilityEvent(int eventType) {
- if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
- return;
- }
-
- // if we are showing a feature that should be announced and one child
- // make this child the event source since this is the feature itself
- // otherwise the callback will take over and announce its client
- if ((mFeatureId == FEATURE_OPTIONS_PANEL ||
- mFeatureId == FEATURE_CONTEXT_MENU ||
- mFeatureId == FEATURE_PROGRESS ||
- mFeatureId == FEATURE_INDETERMINATE_PROGRESS)
- && getChildCount() == 1) {
- getChildAt(0).sendAccessibilityEvent(eventType);
- } else {
- super.sendAccessibilityEvent(eventType);
- }
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- final Callback cb = mWindow.getCallback();
- if (cb != null && !mWindow.isDestroyed()) {
- if (cb.dispatchPopulateAccessibilityEvent(event)) {
- return true;
- }
- }
- return super.dispatchPopulateAccessibilityEventInternal(event);
- }
-
- @Override
- protected boolean setFrame(int l, int t, int r, int b) {
- boolean changed = super.setFrame(l, t, r, b);
- if (changed) {
- final Rect drawingBounds = mDrawingBounds;
- getDrawingRect(drawingBounds);
-
- Drawable fg = getForeground();
- if (fg != null) {
- final Rect frameOffsets = mFrameOffsets;
- drawingBounds.left += frameOffsets.left;
- drawingBounds.top += frameOffsets.top;
- drawingBounds.right -= frameOffsets.right;
- drawingBounds.bottom -= frameOffsets.bottom;
- fg.setBounds(drawingBounds);
- final Rect framePadding = mFramePadding;
- drawingBounds.left += framePadding.left - frameOffsets.left;
- drawingBounds.top += framePadding.top - frameOffsets.top;
- drawingBounds.right -= framePadding.right - frameOffsets.right;
- drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
- }
-
- Drawable bg = getBackground();
- if (bg != null) {
- bg.setBounds(drawingBounds);
- }
-
- if (SWEEP_OPEN_MENU) {
- if (mMenuBackground == null && mFeatureId < 0
- && mWindow.getAttributes().height
- == WindowManager.LayoutParams.MATCH_PARENT) {
- mMenuBackground = getContext().getDrawable(
- R.drawable.menu_background);
- }
- if (mMenuBackground != null) {
- mMenuBackground.setBounds(drawingBounds.left,
- drawingBounds.bottom-6, drawingBounds.right,
- drawingBounds.bottom+20);
- }
- }
- }
- return changed;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
- final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
-
- final int widthMode = getMode(widthMeasureSpec);
- final int heightMode = getMode(heightMeasureSpec);
-
- boolean fixedWidth = false;
- if (widthMode == AT_MOST) {
- final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
- : mWindow.mFixedWidthMajor;
- if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
- final int w;
- if (tvw.type == TypedValue.TYPE_DIMENSION) {
- w = (int) tvw.getDimension(metrics);
- } else if (tvw.type == TypedValue.TYPE_FRACTION) {
- w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
- } else {
- w = 0;
- }
-
- if (w > 0) {
- final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- Math.min(w, widthSize), EXACTLY);
- fixedWidth = true;
- }
- }
- }
-
- if (heightMode == AT_MOST) {
- final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
- : mWindow.mFixedHeightMinor;
- if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
- final int h;
- if (tvh.type == TypedValue.TYPE_DIMENSION) {
- h = (int) tvh.getDimension(metrics);
- } else if (tvh.type == TypedValue.TYPE_FRACTION) {
- h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
- } else {
- h = 0;
- }
- if (h > 0) {
- final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- Math.min(h, heightSize), EXACTLY);
- }
- }
- }
-
- getOutsets(mWindow.mOutsets);
- if (mWindow.mOutsets.top > 0 || mWindow.mOutsets.bottom > 0) {
- int mode = MeasureSpec.getMode(heightMeasureSpec);
- if (mode != MeasureSpec.UNSPECIFIED) {
- int height = MeasureSpec.getSize(heightMeasureSpec);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- height + mWindow.mOutsets.top + mWindow.mOutsets.bottom, mode);
- }
- }
- if (mWindow.mOutsets.left > 0 || mWindow.mOutsets.right > 0) {
- int mode = MeasureSpec.getMode(widthMeasureSpec);
- if (mode != MeasureSpec.UNSPECIFIED) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- width + mWindow.mOutsets.left + mWindow.mOutsets.right, mode);
- }
- }
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- int width = getMeasuredWidth();
- boolean measure = false;
-
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
-
- if (!fixedWidth && widthMode == AT_MOST) {
- final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
- if (tv.type != TypedValue.TYPE_NULL) {
- final int min;
- if (tv.type == TypedValue.TYPE_DIMENSION) {
- min = (int)tv.getDimension(metrics);
- } else if (tv.type == TypedValue.TYPE_FRACTION) {
- min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
- } else {
- min = 0;
- }
-
- if (width < min) {
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
- measure = true;
- }
- }
- }
-
- // TODO: Support height?
-
- if (measure) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- getOutsets(mWindow.mOutsets);
- if (mWindow.mOutsets.left > 0) {
- offsetLeftAndRight(-mWindow.mOutsets.left);
- }
- if (mWindow.mOutsets.top > 0) {
- offsetTopAndBottom(-mWindow.mOutsets.top);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- if (mMenuBackground != null) {
- mMenuBackground.draw(canvas);
- }
- }
-
- @Override
- public boolean showContextMenuForChild(View originalView) {
- // Reuse the context menu builder
- if (mWindow.mContextMenu == null) {
- mWindow.mContextMenu = new ContextMenuBuilder(getContext());
- mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
- } else {
- mWindow.mContextMenu.clearAll();
- }
-
- final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
- originalView.getWindowToken());
- if (helper != null) {
- helper.setPresenterCallback(mWindow.mContextMenuCallback);
- } else if (mWindow.mContextMenuHelper != null) {
- // No menu to show, but if we have a menu currently showing it just became blank.
- // Close it.
- mWindow.mContextMenuHelper.dismiss();
- }
- mWindow.mContextMenuHelper = helper;
- return helper != null;
- }
-
- @Override
- public boolean showContextMenuForChild(View originalView, float x, float y) {
- // Reuse the context menu builder
- if (mWindow.mContextMenu == null) {
- mWindow.mContextMenu = new ContextMenuBuilder(getContext());
- mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
- } else {
- mWindow.mContextMenu.clearAll();
- }
-
- final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
- getContext(), originalView, x, y);
- if (helper != null) {
- helper.setCallback(mWindow.mContextMenuCallback);
- } else if (mWindow.mContextMenuPopupHelper != null) {
- // No menu to show, but if we have a menu currently showing it just became blank.
- // Close it.
- mWindow.mContextMenuPopupHelper.dismiss();
- }
- mWindow.mContextMenuPopupHelper = helper;
- return helper != null;
- }
-
- @Override
- public ActionMode startActionModeForChild(View originalView,
- ActionMode.Callback callback) {
- return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
- }
-
- @Override
- public ActionMode startActionModeForChild(
- View child, ActionMode.Callback callback, int type) {
- return startActionMode(child, callback, type);
- }
-
- @Override
- public ActionMode startActionMode(ActionMode.Callback callback) {
- return startActionMode(callback, ActionMode.TYPE_PRIMARY);
- }
-
- @Override
- public ActionMode startActionMode(ActionMode.Callback callback, int type) {
- return startActionMode(this, callback, type);
- }
-
- private ActionMode startActionMode(
- View originatingView, ActionMode.Callback callback, int type) {
- ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
- ActionMode mode = null;
- if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
- try {
- mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
- } catch (AbstractMethodError ame) {
- // Older apps might not implement the typed version of this method.
- if (type == ActionMode.TYPE_PRIMARY) {
- try {
- mode = mWindow.getCallback().onWindowStartingActionMode(
- wrappedCallback);
- } catch (AbstractMethodError ame2) {
- // Older apps might not implement this callback method at all.
- }
- }
- }
- }
- if (mode != null) {
- if (mode.getType() == ActionMode.TYPE_PRIMARY) {
- cleanupPrimaryActionMode();
- mPrimaryActionMode = mode;
- } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
- if (mFloatingActionMode != null) {
- mFloatingActionMode.finish();
- }
- mFloatingActionMode = mode;
- }
- } else {
- mode = createActionMode(type, wrappedCallback, originatingView);
- if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
- setHandledActionMode(mode);
- } else {
- mode = null;
- }
- }
- if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
- try {
- mWindow.getCallback().onActionModeStarted(mode);
- } catch (AbstractMethodError ame) {
- // Older apps might not implement this callback method.
- }
- }
- return mode;
- }
-
- private void cleanupPrimaryActionMode() {
- if (mPrimaryActionMode != null) {
- mPrimaryActionMode.finish();
- mPrimaryActionMode = null;
- }
- if (mPrimaryActionModeView != null) {
- mPrimaryActionModeView.killMode();
- }
- }
-
- private void cleanupFloatingActionModeViews() {
- if (mFloatingToolbar != null) {
- mFloatingToolbar.dismiss();
- mFloatingToolbar = null;
- }
- if (mFloatingActionModeOriginatingView != null) {
- if (mFloatingToolbarPreDrawListener != null) {
- mFloatingActionModeOriginatingView.getViewTreeObserver()
- .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
- mFloatingToolbarPreDrawListener = null;
- }
- mFloatingActionModeOriginatingView = null;
- }
- }
-
- public void startChanging() {
- mChanging = true;
- }
-
- public void finishChanging() {
- mChanging = false;
- drawableChanged();
- }
-
- public void setWindowBackground(Drawable drawable) {
- if (getBackground() != drawable) {
- setBackgroundDrawable(drawable);
- if (drawable != null) {
- drawable.getPadding(mBackgroundPadding);
- } else {
- mBackgroundPadding.setEmpty();
- }
- drawableChanged();
- }
- }
-
- public void setWindowFrame(Drawable drawable) {
- if (getForeground() != drawable) {
- setForeground(drawable);
- if (drawable != null) {
- drawable.getPadding(mFramePadding);
- } else {
- mFramePadding.setEmpty();
- }
- drawableChanged();
- }
- }
-
- @Override
- public void onWindowSystemUiVisibilityChanged(int visible) {
- updateColorViews(null /* insets */, true /* animate */);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mFrameOffsets.set(insets.getSystemWindowInsets());
- insets = updateColorViews(insets, true /* animate */);
- insets = updateStatusGuard(insets);
- updateNavigationGuard(insets);
- if (getForeground() != null) {
- drawableChanged();
- }
- return insets;
- }
-
- @Override
- public boolean isTransitionGroup() {
- return false;
- }
-
- private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
- WindowManager.LayoutParams attrs = mWindow.getAttributes();
- int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
-
- if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
- boolean disallowAnimate = !isLaidOut();
- disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
- & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- mLastWindowFlags = attrs.flags;
-
- if (insets != null) {
- mLastTopInset = Math.min(insets.getStableInsetTop(),
- insets.getSystemWindowInsetTop());
- mLastBottomInset = Math.min(insets.getStableInsetBottom(),
- insets.getSystemWindowInsetBottom());
- mLastRightInset = Math.min(insets.getStableInsetRight(),
- insets.getSystemWindowInsetRight());
-
- // Don't animate if the presence of stable insets has changed, because that
- // indicates that the window was either just added and received them for the
- // first time, or the window size or position has changed.
- boolean hasTopStableInset = insets.getStableInsetTop() != 0;
- disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
- mLastHasTopStableInset = hasTopStableInset;
-
- boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
- disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
- mLastHasBottomStableInset = hasBottomStableInset;
-
- boolean hasRightStableInset = insets.getStableInsetRight() != 0;
- disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
- mLastHasRightStableInset = hasRightStableInset;
- }
-
- boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
- int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
- updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
- mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
- 0 /* rightInset */, animate && !disallowAnimate);
-
- boolean statusBarNeedsRightInset = navBarToRightEdge
- && mNavigationColorViewState.present;
- int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
- updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
- mLastTopInset, false /* matchVertical */, statusBarRightInset,
- animate && !disallowAnimate);
- }
-
- // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
- // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
- // explicitly asked for it.
-
- boolean consumingNavBar =
- (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
- && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
-
- int consumedRight = consumingNavBar ? mLastRightInset : 0;
- int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
-
- if (mWindow.mContentRoot != null
- && mWindow.mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
- MarginLayoutParams lp = (MarginLayoutParams) mWindow.mContentRoot.getLayoutParams();
- if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
- lp.rightMargin = consumedRight;
- lp.bottomMargin = consumedBottom;
- mWindow.mContentRoot.setLayoutParams(lp);
-
- if (insets == null) {
- // The insets have changed, but we're not currently in the process
- // of dispatching them.
- requestApplyInsets();
- }
- }
- if (insets != null) {
- insets = insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight() - consumedRight,
- insets.getSystemWindowInsetBottom() - consumedBottom);
- }
- }
-
- if (insets != null) {
- insets = insets.consumeStableInsets();
- }
- return insets;
- }
-
- /**
- * Update a color view
- *
- * @param state the color view to update.
- * @param sysUiVis the current systemUiVisibility to apply.
- * @param color the current color to apply.
- * @param size the current size in the non-parent-matching dimension.
- * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
- * horizontal edge,
- * @param rightMargin rightMargin for the color view.
- * @param animate if true, the change will be animated.
- */
- private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
- int size, boolean verticalBar, int rightMargin, boolean animate) {
- state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
- && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
- && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
- boolean show = state.present
- && (color & Color.BLACK) != 0
- && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
-
- boolean visibilityChanged = false;
- View view = state.view;
-
- int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
- int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
- int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
-
- if (view == null) {
- if (show) {
- state.view = view = new View(mContext);
- view.setBackgroundColor(color);
- view.setTransitionName(state.transitionName);
- view.setId(state.id);
- visibilityChanged = true;
- view.setVisibility(INVISIBLE);
- state.targetVisibility = VISIBLE;
-
- LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
- resolvedGravity);
- lp.rightMargin = rightMargin;
- addView(view, lp);
- updateColorViewTranslations();
- }
- } else {
- int vis = show ? VISIBLE : INVISIBLE;
- visibilityChanged = state.targetVisibility != vis;
- state.targetVisibility = vis;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- if (lp.height != resolvedHeight || lp.width != resolvedWidth
- || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
- lp.height = resolvedHeight;
- lp.width = resolvedWidth;
- lp.gravity = resolvedGravity;
- lp.rightMargin = rightMargin;
- view.setLayoutParams(lp);
- }
- if (show) {
- view.setBackgroundColor(color);
- }
- }
- if (visibilityChanged) {
- view.animate().cancel();
- if (animate) {
- if (show) {
- if (view.getVisibility() != VISIBLE) {
- view.setVisibility(VISIBLE);
- view.setAlpha(0.0f);
- }
- view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
- setDuration(mBarEnterExitDuration);
- } else {
- view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
- .setDuration(mBarEnterExitDuration)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- state.view.setAlpha(1.0f);
- state.view.setVisibility(INVISIBLE);
- }
- });
- }
- } else {
- view.setAlpha(1.0f);
- view.setVisibility(show ? VISIBLE : INVISIBLE);
- }
- }
- }
-
- private void updateColorViewTranslations() {
- // Put the color views back in place when they get moved off the screen
- // due to the the ViewRootImpl panning.
- int rootScrollY = mRootScrollY;
- if (mStatusColorViewState.view != null) {
- mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
- }
- if (mNavigationColorViewState.view != null) {
- mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
- }
- }
-
- private WindowInsets updateStatusGuard(WindowInsets insets) {
- boolean showStatusGuard = false;
- // Show the status guard when the non-overlay contextual action bar is showing
- if (mPrimaryActionModeView != null) {
- if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
- // Insets are magic!
- final MarginLayoutParams mlp = (MarginLayoutParams)
- mPrimaryActionModeView.getLayoutParams();
- boolean mlpChanged = false;
- if (mPrimaryActionModeView.isShown()) {
- if (mWindow.mTempRect == null) {
- mWindow.mTempRect = new Rect();
- }
- final Rect rect = mWindow.mTempRect;
-
- // If the parent doesn't consume the insets, manually
- // apply the default system window insets.
- mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
- final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
- if (mlp.topMargin != newMargin) {
- mlpChanged = true;
- mlp.topMargin = insets.getSystemWindowInsetTop();
-
- if (mStatusGuard == null) {
- mStatusGuard = new View(mContext);
- mStatusGuard.setBackgroundColor(mContext.getColor(
- R.color.input_method_navigation_guard));
- addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
- new LayoutParams(LayoutParams.MATCH_PARENT,
- mlp.topMargin, Gravity.START | Gravity.TOP));
- } else {
- final LayoutParams lp = (LayoutParams)
- mStatusGuard.getLayoutParams();
- if (lp.height != mlp.topMargin) {
- lp.height = mlp.topMargin;
- mStatusGuard.setLayoutParams(lp);
- }
- }
- }
-
- // The action mode's theme may differ from the app, so
- // always show the status guard above it if we have one.
- showStatusGuard = mStatusGuard != null;
-
- // We only need to consume the insets if the action
- // mode is overlaid on the app content (e.g. it's
- // sitting in a FrameLayout, see
- // screen_simple_overlay_action_mode.xml).
- final boolean nonOverlay = (mWindow.getLocalFeatures()
- & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0;
- insets = insets.consumeSystemWindowInsets(
- false, nonOverlay && showStatusGuard /* top */, false, false);
- } else {
- // reset top margin
- if (mlp.topMargin != 0) {
- mlpChanged = true;
- mlp.topMargin = 0;
- }
- }
- if (mlpChanged) {
- mPrimaryActionModeView.setLayoutParams(mlp);
- }
- }
- }
- if (mStatusGuard != null) {
- mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
- }
- return insets;
- }
-
- private void updateNavigationGuard(WindowInsets insets) {
- // IMEs lay out below the nav bar, but the content view must not (for back compat)
- if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
- // prevent the content view from including the nav bar height
- if (mWindow.mContentParent != null) {
- if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
- MarginLayoutParams mlp =
- (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
- mlp.bottomMargin = insets.getSystemWindowInsetBottom();
- mWindow.mContentParent.setLayoutParams(mlp);
- }
- }
- // position the navigation guard view, creating it if necessary
- if (mNavigationGuard == null) {
- mNavigationGuard = new View(mContext);
- mNavigationGuard.setBackgroundColor(mContext.getColor(
- R.color.input_method_navigation_guard));
- addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
- new LayoutParams(LayoutParams.MATCH_PARENT,
- insets.getSystemWindowInsetBottom(),
- Gravity.START | Gravity.BOTTOM));
- } else {
- LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
- lp.height = insets.getSystemWindowInsetBottom();
- mNavigationGuard.setLayoutParams(lp);
- }
- }
- }
-
- private void drawableChanged() {
- if (mChanging) {
- return;
- }
-
- setPadding(mFramePadding.left + mBackgroundPadding.left,
- mFramePadding.top + mBackgroundPadding.top,
- mFramePadding.right + mBackgroundPadding.right,
- mFramePadding.bottom + mBackgroundPadding.bottom);
- requestLayout();
- invalidate();
-
- int opacity = PixelFormat.OPAQUE;
- if (windowHasShadow()) {
- // If the window has a shadow, it must be translucent.
- opacity = PixelFormat.TRANSLUCENT;
- } else{
- // Note: If there is no background, we will assume opaque. The
- // common case seems to be that an application sets there to be
- // no background so it can draw everything itself. For that,
- // we would like to assume OPAQUE and let the app force it to
- // the slower TRANSLUCENT mode if that is really what it wants.
- Drawable bg = getBackground();
- Drawable fg = getForeground();
- if (bg != null) {
- if (fg == null) {
- opacity = bg.getOpacity();
- } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
- && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
- // If the frame padding is zero, then we can be opaque
- // if either the frame -or- the background is opaque.
- int fop = fg.getOpacity();
- int bop = bg.getOpacity();
- if (false)
- Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
- if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
- opacity = PixelFormat.OPAQUE;
- } else if (fop == PixelFormat.UNKNOWN) {
- opacity = bop;
- } else if (bop == PixelFormat.UNKNOWN) {
- opacity = fop;
- } else {
- opacity = Drawable.resolveOpacity(fop, bop);
- }
- } else {
- // For now we have to assume translucent if there is a
- // frame with padding... there is no way to tell if the
- // frame and background together will draw all pixels.
- if (false)
- Log.v(TAG, "Padding: " + mFramePadding);
- opacity = PixelFormat.TRANSLUCENT;
- }
- }
- if (false)
- Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
- }
-
- if (false)
- Log.v(TAG, "Selected default opacity: " + opacity);
-
- mDefaultOpacity = opacity;
- if (mFeatureId < 0) {
- mWindow.setDefaultWindowFormat(opacity);
- }
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
-
- // If the user is chording a menu shortcut, release the chord since
- // this window lost focus
- if (mWindow.hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus
- && mWindow.mPanelChordingKey != 0) {
- mWindow.closePanel(FEATURE_OPTIONS_PANEL);
- }
-
- final Callback cb = mWindow.getCallback();
- if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
- cb.onWindowFocusChanged(hasWindowFocus);
- }
-
- if (mPrimaryActionMode != null) {
- mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
- }
- if (mFloatingActionMode != null) {
- mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- final Callback cb = mWindow.getCallback();
- if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
- cb.onAttachedToWindow();
- }
-
- if (mFeatureId == -1) {
- /*
- * The main window has been attached, try to restore any panels
- * that may have been open before. This is called in cases where
- * an activity is being killed for configuration change and the
- * menu was open. When the activity is recreated, the menu
- * should be shown again.
- */
- mWindow.openPanelsAfterRestore();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- final Callback cb = mWindow.getCallback();
- if (cb != null && mFeatureId < 0) {
- cb.onDetachedFromWindow();
- }
-
- if (mWindow.mDecorContentParent != null) {
- mWindow.mDecorContentParent.dismissPopups();
- }
-
- if (mPrimaryActionModePopup != null) {
- removeCallbacks(mShowPrimaryActionModePopup);
- if (mPrimaryActionModePopup.isShowing()) {
- mPrimaryActionModePopup.dismiss();
- }
- mPrimaryActionModePopup = null;
- }
- if (mFloatingToolbar != null) {
- mFloatingToolbar.dismiss();
- mFloatingToolbar = null;
- }
-
- PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (st != null && st.menu != null && mFeatureId < 0) {
- st.menu.close();
- }
- }
-
- @Override
- public void onCloseSystemDialogs(String reason) {
- if (mFeatureId >= 0) {
- mWindow.closeAllPanels();
- }
- }
-
- public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
- return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
- }
-
- public InputQueue.Callback willYouTakeTheInputQueue() {
- return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
- }
-
- public void setSurfaceType(int type) {
- mWindow.setType(type);
- }
-
- public void setSurfaceFormat(int format) {
- mWindow.setFormat(format);
- }
-
- public void setSurfaceKeepScreenOn(boolean keepOn) {
- if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- @Override
- public void onRootViewScrollYChanged(int rootScrollY) {
- mRootScrollY = rootScrollY;
- updateColorViewTranslations();
- }
-
- private ActionMode createActionMode(
- int type, ActionMode.Callback2 callback, View originatingView) {
- switch (type) {
- case ActionMode.TYPE_PRIMARY:
- default:
- return createStandaloneActionMode(callback);
- case ActionMode.TYPE_FLOATING:
- return createFloatingActionMode(originatingView, callback);
- }
- }
-
- private void setHandledActionMode(ActionMode mode) {
- if (mode.getType() == ActionMode.TYPE_PRIMARY) {
- setHandledPrimaryActionMode(mode);
- } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
- setHandledFloatingActionMode(mode);
- }
- }
-
- private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
- endOnGoingFadeAnimation();
- cleanupPrimaryActionMode();
- if (mPrimaryActionModeView == null) {
- if (mWindow.isFloating()) {
- // Use the action bar theme.
- final TypedValue outValue = new TypedValue();
- final Theme baseTheme = mContext.getTheme();
- baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
- final Context actionBarContext;
- if (outValue.resourceId != 0) {
- final Theme actionBarTheme = mContext.getResources().newTheme();
- actionBarTheme.setTo(baseTheme);
- actionBarTheme.applyStyle(outValue.resourceId, true);
-
- actionBarContext = new ContextThemeWrapper(mContext, 0);
- actionBarContext.getTheme().setTo(actionBarTheme);
- } else {
- actionBarContext = mContext;
- }
-
- mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
- mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
- R.attr.actionModePopupWindowStyle);
- mPrimaryActionModePopup.setWindowLayoutType(
- WindowManager.LayoutParams.TYPE_APPLICATION);
- mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
- mPrimaryActionModePopup.setWidth(MATCH_PARENT);
-
- actionBarContext.getTheme().resolveAttribute(
- R.attr.actionBarSize, outValue, true);
- final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
- actionBarContext.getResources().getDisplayMetrics());
- mPrimaryActionModeView.setContentHeight(height);
- mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
- mShowPrimaryActionModePopup = new Runnable() {
- public void run() {
- mPrimaryActionModePopup.showAtLocation(
- mPrimaryActionModeView.getApplicationWindowToken(),
- Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
- endOnGoingFadeAnimation();
- mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
- 0f, 1f);
- mFadeAnim.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- mPrimaryActionModeView.setVisibility(VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mPrimaryActionModeView.setAlpha(1f);
- mFadeAnim = null;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
-
- }
- });
- mFadeAnim.start();
- }
- };
- } else {
- ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
- if (stub != null) {
- mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
- }
- }
- }
- if (mPrimaryActionModeView != null) {
- mPrimaryActionModeView.killMode();
- ActionMode mode = new StandaloneActionMode(
- mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
- callback, mPrimaryActionModePopup == null);
- return mode;
- }
- return null;
- }
-
- private void endOnGoingFadeAnimation() {
- if (mFadeAnim != null) {
- mFadeAnim.end();
- }
- }
-
- private void setHandledPrimaryActionMode(ActionMode mode) {
- endOnGoingFadeAnimation();
- mPrimaryActionMode = mode;
- mPrimaryActionMode.invalidate();
- mPrimaryActionModeView.initForMode(mPrimaryActionMode);
- if (mPrimaryActionModePopup != null) {
- post(mShowPrimaryActionModePopup);
- } else {
- mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
- mFadeAnim.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- mPrimaryActionModeView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mPrimaryActionModeView.setAlpha(1f);
- mFadeAnim = null;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
-
- }
- });
- mFadeAnim.start();
- }
- mPrimaryActionModeView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
-
- private ActionMode createFloatingActionMode(
- View originatingView, ActionMode.Callback2 callback) {
- if (mFloatingActionMode != null) {
- mFloatingActionMode.finish();
- }
- cleanupFloatingActionModeViews();
- final FloatingActionMode mode =
- new FloatingActionMode(mContext, callback, originatingView);
- mFloatingActionModeOriginatingView = originatingView;
- mFloatingToolbarPreDrawListener =
- new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mode.updateViewLocationInWindow();
- return true;
- }
- };
- return mode;
- }
-
- private void setHandledFloatingActionMode(ActionMode mode) {
- mFloatingActionMode = mode;
- mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
- ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
- mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
- mFloatingActionModeOriginatingView.getViewTreeObserver()
- .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
- }
-
- /**
- * Informs the decor if a non client decor is attached and visible.
- * @param attachedAndVisible true when the decor is visible.
- * Note that this will even be called if there is no non client decor.
- **/
- void enableNonClientDecor(boolean attachedAndVisible) {
- if (mHasNonClientDecor != attachedAndVisible) {
- mHasNonClientDecor = attachedAndVisible;
- if (getForeground() != null) {
- drawableChanged();
- }
- }
- }
-
- /**
- * Returns true if the window has a non client decor.
- * @return If there is a non client decor - even if it is not visible.
- **/
- private boolean windowHasNonClientDecor() {
- return mHasNonClientDecor;
- }
-
- /**
- * Returns true if the Window is free floating and has a shadow (although at some times
- * it might not be displaying it, e.g. during a resize). Note that non overlapping windows
- * do not have a shadow since it could not be seen anyways (a small screen / tablet
- * "tiles" the windows side by side but does not overlap them).
- * @return Returns true when the window has a shadow created by the non client decor.
- **/
- private boolean windowHasShadow() {
- return windowHasNonClientDecor() && StackId.hasWindowShadow(mWindow.mWorkspaceId);
- }
-
- void setWindow(PhoneWindow phoneWindow) {
- mWindow = phoneWindow;
- Context context = getContext();
- if (context instanceof DecorContext) {
- DecorContext decorContex = (DecorContext) context;
- decorContex.setPhoneWindow(mWindow);
- }
- }
-
- /**
- * Clears out internal references when the action mode is destroyed.
- */
- private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
- private final ActionMode.Callback mWrapped;
-
- public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
- mWrapped = wrapped;
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return mWrapped.onCreateActionMode(mode, menu);
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- requestFitSystemWindows();
- return mWrapped.onPrepareActionMode(mode, menu);
- }
-
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return mWrapped.onActionItemClicked(mode, item);
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- mWrapped.onDestroyActionMode(mode);
- final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
- >= Build.VERSION_CODES.M;
- final boolean isPrimary;
- final boolean isFloating;
- if (isMncApp) {
- isPrimary = mode == mPrimaryActionMode;
- isFloating = mode == mFloatingActionMode;
- if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
- Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
- + mode + " was not the current primary action mode! Expected "
- + mPrimaryActionMode);
- }
- if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
- Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
- + mode + " was not the current floating action mode! Expected "
- + mFloatingActionMode);
- }
- } else {
- isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
- isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
- }
- if (isPrimary) {
- if (mPrimaryActionModePopup != null) {
- removeCallbacks(mShowPrimaryActionModePopup);
- }
- if (mPrimaryActionModeView != null) {
- endOnGoingFadeAnimation();
- mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
- 1f, 0f);
- mFadeAnim.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
-
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mPrimaryActionModeView.setVisibility(GONE);
- if (mPrimaryActionModePopup != null) {
- mPrimaryActionModePopup.dismiss();
- }
- mPrimaryActionModeView.removeAllViews();
- mFadeAnim = null;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
-
- }
- });
- mFadeAnim.start();
- }
-
- mPrimaryActionMode = null;
- } else if (isFloating) {
- cleanupFloatingActionModeViews();
- mFloatingActionMode = null;
- }
- if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
- try {
- mWindow.getCallback().onActionModeFinished(mode);
- } catch (AbstractMethodError ame) {
- // Older apps might not implement this callback method.
- }
- }
- requestFitSystemWindows();
- }
-
- @Override
- public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
- if (mWrapped instanceof ActionMode.Callback2) {
- ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
- } else {
- super.onGetContentRect(mode, view, outRect);
- }
- }
- }
- }
-
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
@@ -4065,7 +2485,7 @@
+ Integer.toHexString(mFrameResource));
}
}
- if (mLoadEleveation) {
+ if (mLoadElevation) {
mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
}
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
@@ -4135,19 +2555,7 @@
}
mDecor.startChanging();
-
- mNonClientDecorView = createNonClientDecorView();
- View in = mLayoutInflater.inflate(layoutResource, null);
- if (mNonClientDecorView != null) {
- if (mNonClientDecorView.getParent() == null) {
- decor.addView(mNonClientDecorView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mNonClientDecorView.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- } else {
- decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mContentRoot = (ViewGroup) in;
+ final View in = mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
@@ -4202,50 +2610,6 @@
return contentParent;
}
- // Free floating overlapping windows require a non client decor with a caption and shadow..
- private NonClientDecorView createNonClientDecorView() {
- NonClientDecorView nonClientDecorView = null;
- for (int i = mDecor.getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
- View view = mDecor.getChildAt(i);
- if (view instanceof NonClientDecorView) {
- // The decor was most likely saved from a relaunch - so reuse it.
- nonClientDecorView = (NonClientDecorView) view;
- mDecor.removeViewAt(i);
- }
- }
- final WindowManager.LayoutParams attrs = getAttributes();
- boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
- attrs.type == TYPE_APPLICATION;
- mWorkspaceId = getWorkspaceId();
- // Only a non floating application window on one of the allowed workspaces can get a non
- // client decor.
- if (!isFloating() && isApplication && StackId.isStaticStack(mWorkspaceId)) {
- // Dependent on the brightness of the used title we either use the
- // dark or the light button frame.
- if (nonClientDecorView == null) {
- Context context = mDecor.getContext();
- TypedValue value = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
- LayoutInflater inflater = mLayoutInflater.from(context);
- if (Color.luminance(value.data) < 0.5) {
- nonClientDecorView = (NonClientDecorView) inflater.inflate(
- R.layout.non_client_decor_dark, null);
- } else {
- nonClientDecorView = (NonClientDecorView) inflater.inflate(
- R.layout.non_client_decor_light, null);
- }
- }
- nonClientDecorView.setPhoneWindow(this, StackId.hasWindowDecor(mWorkspaceId),
- StackId.hasWindowShadow(mWorkspaceId), getResizingBackgroundDrawable(),
- mDecor.getContext().getDrawable(R.drawable.non_client_decor_title_focused));
- }
- // Tell the decor if it has a visible non client decor.
- mDecor.enableNonClientDecor(
- nonClientDecorView != null&& StackId.hasWindowDecor(mWorkspaceId));
-
- return nonClientDecorView;
- }
-
/** @hide */
public void alwaysReadCloseOnTouchAttr() {
mAlwaysReadCloseOnTouchAttr = true;
@@ -4450,7 +2814,7 @@
* isn't in our features, this throws an exception).
* @return The panel state.
*/
- private PanelFeatureState getPanelState(int featureId, boolean required) {
+ PanelFeatureState getPanelState(int featureId, boolean required) {
return getPanelState(featureId, required, null);
}
@@ -4914,7 +3278,7 @@
int curAlpha = 255;
}
- private static final class PanelFeatureState {
+ static final class PanelFeatureState {
/** Feature ID for this panel. */
int featureId;
@@ -5316,30 +3680,12 @@
}
}
- private static class ColorViewState {
- View view = null;
- int targetVisibility = View.INVISIBLE;
- boolean present = false;
+ int getLocalFeaturesPrivate() {
+ return super.getLocalFeatures();
+ }
- final int id;
- final int systemUiHideFlag;
- final int translucentFlag;
- final int verticalGravity;
- final int horizontalGravity;
- final String transitionName;
- final int hideWindowFlag;
-
- ColorViewState(int systemUiHideFlag,
- int translucentFlag, int verticalGravity, int horizontalGravity,
- String transitionName, int id, int hideWindowFlag) {
- this.id = id;
- this.systemUiHideFlag = systemUiHideFlag;
- this.translucentFlag = translucentFlag;
- this.verticalGravity = verticalGravity;
- this.horizontalGravity = horizontalGravity;
- this.transitionName = transitionName;
- this.hideWindowFlag = hideWindowFlag;
- }
+ protected void setDefaultWindowFormat(int format) {
+ super.setDefaultWindowFormat(format);
}
void sendCloseSystemWindows() {
@@ -5391,28 +3737,6 @@
mIsStartingWindow = isStartingWindow;
}
- /**
- * Returns the Id of the workspace which contains this window.
- * Note that if no workspace can be determined - which usually means that it was not
- * created for an activity - the fullscreen workspace ID will be returned.
- * @return Returns the workspace stack id which contains this window.
- **/
- private int getWorkspaceId() {
- int workspaceId = INVALID_STACK_ID;
- WindowControllerCallback callback = getWindowControllerCallback();
- if (callback != null) {
- try {
- workspaceId = callback.getWindowStackId();
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
- }
- }
- if (workspaceId == INVALID_STACK_ID) {
- return FULLSCREEN_WORKSPACE_STACK_ID;
- }
- return workspaceId;
- }
-
@Override
public void setTheme(int resid) {
mTheme = resid;
@@ -5423,31 +3747,4 @@
}
}
}
-
- /**
- * Returns the color used to fill areas the app has not rendered content to yet when the user
- * is resizing the window of an activity in multi-window mode.
- * */
- private Drawable getResizingBackgroundDrawable() {
- final Context context = mDecor.getContext();
-
- if (mBackgroundResource != 0) {
- final Drawable drawable = context.getDrawable(mBackgroundResource);
- if (drawable != null) {
- return drawable;
- }
- }
-
- if (mBackgroundFallbackResource != 0) {
- final Drawable fallbackDrawable = context.getDrawable(mBackgroundFallbackResource);
- if (fallbackDrawable != null) {
- return fallbackDrawable;
- }
- }
-
- // We shouldn't really get here as the background fallback should be always available since
- // it is defaulted by the system.
- Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + this);
- return null;
- }
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f190d8c..e8970bc 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -144,6 +144,13 @@
}
/**
+ * Checks if given array is null or has zero elements.
+ */
+ public static boolean isEmpty(byte[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /**
* Checks that value is present as at least one of the elements of the array.
* @param array the array to check in
* @param value the value to check for
diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java
index 3c7b7ac..7be95d8 100644
--- a/core/java/com/android/internal/util/HexDump.java
+++ b/core/java/com/android/internal/util/HexDump.java
@@ -19,28 +19,29 @@
public class HexDump
{
private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
+ private final static char[] HEX_LOWER_CASE_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
public static String dumpHexString(byte[] array)
{
return dumpHexString(array, 0, array.length);
}
-
+
public static String dumpHexString(byte[] array, int offset, int length)
{
StringBuilder result = new StringBuilder();
-
+
byte[] line = new byte[16];
int lineIndex = 0;
-
+
result.append("\n0x");
result.append(toHexString(offset));
-
+
for (int i = offset ; i < offset + length ; i++)
{
if (lineIndex == 16)
{
result.append(" ");
-
+
for (int j = 0 ; j < 16 ; j++)
{
if (line[j] > ' ' && line[j] < '~')
@@ -52,20 +53,20 @@
result.append(".");
}
}
-
+
result.append("\n0x");
result.append(toHexString(i));
lineIndex = 0;
}
-
+
byte b = array[i];
result.append(" ");
result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
result.append(HEX_DIGITS[b & 0x0F]);
-
+
line[lineIndex++] = b;
}
-
+
if (lineIndex != 16)
{
int count = (16 - lineIndex) * 3;
@@ -74,7 +75,7 @@
{
result.append(" ");
}
-
+
for (int i = 0 ; i < lineIndex ; i++)
{
if (line[i] > ' ' && line[i] < '~')
@@ -87,10 +88,10 @@
}
}
}
-
+
return result.toString();
}
-
+
public static String toHexString(byte b)
{
return toHexString(toByteArray(b));
@@ -98,48 +99,59 @@
public static String toHexString(byte[] array)
{
- return toHexString(array, 0, array.length);
+ return toHexString(array, 0, array.length, true);
}
-
+
+ public static String toHexString(byte[] array, boolean upperCase)
+ {
+ return toHexString(array, 0, array.length, upperCase);
+ }
+
public static String toHexString(byte[] array, int offset, int length)
{
+ return toHexString(array, offset, length, true);
+ }
+
+ public static String toHexString(byte[] array, int offset, int length, boolean upperCase)
+ {
+ char[] digits = upperCase ? HEX_DIGITS : HEX_LOWER_CASE_DIGITS;
char[] buf = new char[length * 2];
int bufIndex = 0;
- for (int i = offset ; i < offset + length; i++)
+ for (int i = offset ; i < offset + length; i++)
{
byte b = array[i];
- buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
- buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
+ buf[bufIndex++] = digits[(b >>> 4) & 0x0F];
+ buf[bufIndex++] = digits[b & 0x0F];
}
- return new String(buf);
+ return new String(buf);
}
-
+
public static String toHexString(int i)
{
return toHexString(toByteArray(i));
}
-
+
public static byte[] toByteArray(byte b)
{
byte[] array = new byte[1];
array[0] = b;
return array;
}
-
+
public static byte[] toByteArray(int i)
{
byte[] array = new byte[4];
-
+
array[3] = (byte)(i & 0xFF);
array[2] = (byte)((i >> 8) & 0xFF);
array[1] = (byte)((i >> 16) & 0xFF);
array[0] = (byte)((i >> 24) & 0xFF);
-
+
return array;
}
-
+
private static int toByte(char c)
{
if (c >= '0' && c <= '9') return (c - '0');
@@ -148,7 +160,7 @@
throw new RuntimeException ("Invalid hex char '" + c + "'");
}
-
+
public static byte[] hexStringToByteArray(String hexString)
{
int length = hexString.length();
@@ -158,7 +170,15 @@
{
buffer[i / 2] = (byte)((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i+1)));
}
-
+
return buffer;
- }
+ }
+
+ public static StringBuilder appendByteAsHex(StringBuilder sb, byte b, boolean upperCase) {
+ char[] digits = upperCase ? HEX_DIGITS : HEX_LOWER_CASE_DIGITS;
+ sb.append(digits[(b >> 4) & 0xf]);
+ sb.append(digits[b & 0xf]);
+ return sb;
+ }
+
}
diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index bee59dc..007ab29 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
@@ -38,10 +39,29 @@
mContext = context;
}
+ /**
+ * Returns the maximum number of action buttons that should be permitted within an action
+ * bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit.
+ * "always" items can override this.
+ */
public int getMaxActionButtons() {
- return mContext.getResources().getInteger(R.integer.max_action_buttons);
+ final Configuration config = mContext.getResources().getConfiguration();
+ final int width = config.screenWidthDp;
+ final int height = config.screenHeightDp;
+ final int smallest = config.smallestScreenWidthDp;
+ if (smallest > 600 || (width > 960 && height > 720) || (width > 720 && height > 960)) {
+ // For values-w600dp, values-sw600dp and values-xlarge.
+ return 5;
+ } else if (width >= 500 || (width > 640 && height > 480) || (width > 480 && height > 640)) {
+ // For values-w500dp and values-large.
+ return 4;
+ } else if (width >= 360) {
+ // For values-w360dp.
+ return 3;
+ } else {
+ return 2;
+ }
}
-
public boolean showsOverflowMenuButton() {
return true;
}
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 89d36ff..81056f2 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -25,7 +25,8 @@
oneway interface IInputMethodClient {
void setUsingInputMethod(boolean state);
void onBindMethod(in InputBindResult res);
- void onUnbindMethod(int sequence);
+ // unbindReason corresponds to InputMethodClient.UnbindReason.
+ void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active);
void setUserActionNotificationSequenceNumber(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java
new file mode 100644
index 0000000..a035343
--- /dev/null
+++ b/core/java/com/android/internal/view/InputMethodClient.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+public final class InputMethodClient {
+ public static final int UNBIND_REASON_UNSPECIFIED = 0;
+ public static final int UNBIND_REASON_SWITCH_CLIENT = 1;
+ public static final int UNBIND_REASON_SWITCH_IME = 2;
+ public static final int UNBIND_REASON_DISCONNECT_IME = 3;
+ public static final int UNBIND_REASON_NO_IME = 4;
+ public static final int UNBIND_REASON_SWITCH_IME_FAILED = 5;
+ public static final int UNBIND_REASON_RESET_IME = 6;
+
+ @Retention(SOURCE)
+ @IntDef({UNBIND_REASON_UNSPECIFIED, UNBIND_REASON_SWITCH_CLIENT, UNBIND_REASON_SWITCH_IME,
+ UNBIND_REASON_DISCONNECT_IME, UNBIND_REASON_NO_IME, UNBIND_REASON_SWITCH_IME_FAILED,
+ UNBIND_REASON_RESET_IME})
+ public @interface UnbindReason {}
+
+ public static String getUnbindReason(@UnbindReason final int reason) {
+ switch (reason) {
+ case UNBIND_REASON_UNSPECIFIED:
+ return "UNSPECIFIED";
+ case UNBIND_REASON_SWITCH_CLIENT:
+ return "SWITCH_CLIENT";
+ case UNBIND_REASON_SWITCH_IME:
+ return "SWITCH_IME";
+ case UNBIND_REASON_DISCONNECT_IME:
+ return "DISCONNECT_IME";
+ case UNBIND_REASON_NO_IME:
+ return "NO_IME";
+ case UNBIND_REASON_SWITCH_IME_FAILED:
+ return "SWITCH_IME_FAILED";
+ case UNBIND_REASON_RESET_IME:
+ return "RESET_IME";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index e674ecc..59d5f94 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,82 +18,104 @@
import com.android.internal.view.menu.MenuPresenter.Callback;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
-import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
/**
* Presents a menu as a small, simple popup anchored to another view.
- *
- * @hide
*/
-public class MenuPopupHelper implements PopupWindow.OnDismissListener {
+public class MenuPopupHelper {
private final Context mContext;
+
+ // Immutable cached popup menu properties.
private final MenuBuilder mMenu;
private final boolean mOverflowOnly;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
+ // Mutable cached popup menu properties.
private View mAnchorView;
- private MenuPopup mPopup;
-
- private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private int mDropDownGravity = Gravity.START;
private boolean mForceShowIcon;
- private boolean mShowTitle;
private Callback mPresenterCallback;
- private int mInitXOffset;
- private int mInitYOffset;
- public MenuPopupHelper(Context context, MenuBuilder menu) {
+ private MenuPopup mPopup;
+ private OnDismissListener mOnDismissListener;
+
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView) {
this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly, int popupStyleAttr) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView,
+ boolean overflowOnly, @AttrRes int popupStyleAttr) {
this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
}
- public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
- boolean overflowOnly, int popupStyleAttr, int popupStyleRes) {
+ public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
+ @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr,
+ @StyleRes int popupStyleRes) {
mContext = context;
mMenu = menu;
+ mAnchorView = anchorView;
mOverflowOnly = overflowOnly;
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
- mAnchorView = anchorView;
- mPopup = createMenuPopup();
}
- private MenuPopup createMenuPopup() {
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus)) {
- return new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, mPopupStyleRes,
- mOverflowOnly);
- }
- return new StandardMenuPopup(
- mContext, mMenu, mAnchorView, mPopupStyleAttr, mPopupStyleRes, mOverflowOnly);
+ public void setOnDismissListener(@Nullable OnDismissListener listener) {
+ mOnDismissListener = listener;
}
- public void setAnchorView(View anchor) {
+ /**
+ * Sets the view to which the popup window is anchored.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param anchor the view to which the popup window should be anchored
+ */
+ public void setAnchorView(@NonNull View anchor) {
mAnchorView = anchor;
- mPopup.setAnchorView(anchor);
}
- public void setForceShowIcon(boolean forceShow) {
- mForceShowIcon = forceShow;
- mPopup.setForceShowIcon(forceShow);
+ /**
+ * Sets whether the popup menu's adapter is forced to show icons in the
+ * menu item views.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param forceShowIcon {@code true} to force icons to be shown, or
+ * {@code false} for icons to be optionally shown
+ */
+ public void setForceShowIcon(boolean forceShowIcon) {
+ mForceShowIcon = forceShowIcon;
}
+ /**
+ * Sets the alignment of the popup window relative to the anchor view.
+ * <p>
+ * Changes take effect on the next call to show().
+ *
+ * @param gravity alignment of the popup relative to the anchor
+ */
public void setGravity(int gravity) {
mDropDownGravity = gravity;
- mPopup.setGravity(gravity);
}
+ /**
+ * @return alignment of the popup relative to the anchor
+ */
public int getGravity() {
return mDropDownGravity;
}
@@ -110,7 +132,11 @@
}
}
- public ShowableListMenu getPopup() {
+ @NonNull
+ public MenuPopup getPopup() {
+ if (mPopup == null) {
+ mPopup = createPopup();
+ }
return mPopup;
}
@@ -129,14 +155,28 @@
return false;
}
- mInitXOffset = 0;
- mInitYOffset = 0;
- mShowTitle = false;
-
- showPopup();
+ showPopup(0, 0, false, false);
return true;
}
+ /**
+ * Shows the popup menu and makes a best-effort to anchor it to the
+ * specified (x,y) coordinate relative to the anchor view.
+ * <p>
+ * If the popup's resolved gravity is {@link Gravity#LEFT}, this will
+ * display the popup with its top-left corner at (x,y) relative to the
+ * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
+ * popup's top-right corner will be at (x,y).
+ * <p>
+ * If the popup cannot be displayed fully on-screen, this method will
+ * attempt to scroll the anchor view's ancestors and/or offset the popup
+ * such that it may be displayed fully on-screen.
+ *
+ * @param x x coordinate relative to the anchor view
+ * @param y y coordinate relative to the anchor view
+ * @return {@code true} if the popup was shown or was already showing prior
+ * to calling this method, {@code false} otherwise
+ */
public boolean tryShow(int x, int y) {
if (isShowing()) {
return true;
@@ -146,51 +186,104 @@
return false;
}
- mInitXOffset = x;
- mInitYOffset = y;
- mShowTitle = true;
-
- showPopup();
+ showPopup(x, y, true, true);
return true;
}
- private void showPopup() {
- mPopup = createMenuPopup();
- mPopup.setAnchorView(mAnchorView);
- mPopup.setCallback(mPresenterCallback);
- mPopup.setForceShowIcon(mForceShowIcon);
- mPopup.setGravity(mDropDownGravity);
- mPopup.setHorizontalOffset(mInitXOffset);
- mPopup.setShowTitle(mShowTitle);
- mPopup.setVerticalOffset(mInitYOffset);
+ /**
+ * Creates the popup and assigns cached properties.
+ *
+ * @return an initialized popup
+ */
+ @NonNull
+ private MenuPopup createPopup() {
+ final boolean enableCascadingSubmenus = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableCascadingSubmenus);
- // In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
- // we must set the listener to this outer Helper rather than to the inner MenuPopup.
- // Not to worry -- the inner MenuPopup will call our own #onDismiss method after it's done
- // its own handling.
- mPopup.setOnDismissListener(this);
+ final MenuPopup popup;
+ if (enableCascadingSubmenus) {
+ popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr,
+ mPopupStyleRes, mOverflowOnly);
+ } else {
+ popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr,
+ mPopupStyleRes, mOverflowOnly);
+ }
- mPopup.addMenu(mMenu);
- mPopup.show();
+ // Assign immutable properties.
+ popup.addMenu(mMenu);
+ popup.setOnDismissListener(mInternalOnDismissListener);
+
+ // Assign mutable properties. These may be reassigned later.
+ popup.setAnchorView(mAnchorView);
+ popup.setCallback(mPresenterCallback);
+ popup.setForceShowIcon(mForceShowIcon);
+ popup.setGravity(mDropDownGravity);
+
+ return popup;
}
+ private void showPopup(int xOffset, int yOffset, boolean resolveOffsets, boolean showTitle) {
+ if (resolveOffsets) {
+ // If the resolved drop-down gravity is RIGHT, the popup's right
+ // edge will be aligned with the anchor view. Adjust by the anchor
+ // width such that the top-right corner is at the X offset.
+ final int hgrav = Gravity.getAbsoluteGravity(mDropDownGravity,
+ mAnchorView.getLayoutDirection()) & Gravity.HORIZONTAL_GRAVITY_MASK;
+ if (hgrav == Gravity.RIGHT) {
+ xOffset -= mAnchorView.getWidth();
+ }
+ }
+
+ final MenuPopup popup = getPopup();
+ popup.setHorizontalOffset(xOffset);
+ popup.setVerticalOffset(yOffset);
+ popup.setShowTitle(showTitle);
+ popup.show();
+ }
+
+ /**
+ * Dismisses the popup, if showing.
+ */
public void dismiss() {
if (isShowing()) {
mPopup.dismiss();
}
}
- @Override
- public void onDismiss() {
+ /**
+ * Called after the popup has been dismissed.
+ * <p>
+ * <strong>Note:</strong> Subclasses should call the super implementation
+ * last to ensure that any necessary tear down has occurred before the
+ * listener specified by {@link #setOnDismissListener(OnDismissListener)}
+ * is called.
+ */
+ protected void onDismiss() {
mPopup = null;
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
public boolean isShowing() {
return mPopup != null && mPopup.isShowing();
}
- public void setCallback(MenuPresenter.Callback cb) {
+ public void setCallback(@Nullable MenuPresenter.Callback cb) {
mPresenterCallback = cb;
- mPopup.setCallback(cb);
+ if (mPopup != null) {
+ mPopup.setCallback(cb);
+ }
}
+
+ /**
+ * Listener used to proxy dismiss callbacks to the helper's owner.
+ */
+ private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() {
+ @Override
+ public void onDismiss() {
+ MenuPopupHelper.this.onDismiss();
+ }
+ };
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index c3a7460..3e65320 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -360,6 +360,11 @@
}
@Override
+ public int findDependentLayoutAxes(View child, int axisFilter) {
+ return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
pullChildren();
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
index 3b7bce4..a694aca 100644
--- a/core/java/com/android/internal/widget/ButtonBarLayout.java
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -30,6 +30,10 @@
* orientation when it can't fit its child views horizontally.
*/
public class ButtonBarLayout extends LinearLayout {
+ // Whether to allow vertically stacked button bars. This is disabled for
+ // configurations with a small (e.g. less than 320dp) screen height. -->
+ private static final int ALLOW_STACKING_MIN_HEIGHT_DP = 320;
+
/** Whether the current configuration allows stacking. */
private boolean mAllowStacking;
@@ -38,8 +42,12 @@
public ButtonBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ final boolean allowStackingDefault =
+ context.getResources().getConfiguration().screenHeightDp
+ >= ALLOW_STACKING_MIN_HEIGHT_DP;
final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
- mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+ mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking,
+ allowStackingDefault);
ta.recycle();
}
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
new file mode 100644
index 0000000..d747686
--- /dev/null
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.Window;
+
+import com.android.internal.R;
+import com.android.internal.policy.PhoneWindow;
+
+import java.util.ArrayList;
+
+/**
+ * This class represents the special screen elements to control a window on freeform
+ * environment.
+ * As such this class handles the following things:
+ * <ul>
+ * <li>The caption, containing the system buttons like maximize, close and such as well as
+ * allowing the user to drag the window around.</li>
+ * </ul>
+ * After creating the view, the function {@link #setPhoneWindow} needs to be called to make
+ * the connection to it's owning PhoneWindow.
+ * Note: At this time the application can change various attributes of the DecorView which
+ * will break things (in settle/unexpected ways):
+ * <ul>
+ * <li>setOutlineProvider</li>
+ * <li>setSurfaceFormat</li>
+ * <li>..</li>
+ * </ul>
+ *
+ * Although this ViewGroup has only two direct sub-Views, its behavior is more complex due to
+ * overlaying caption on the content and drawing.
+ *
+ * First, no matter where the content View gets added, it will always be the first child and the
+ * caption will be the second. This way the caption will always be drawn on top of the content when
+ * overlaying is enabled.
+ *
+ * Second, the touch dispatch is customized to handle overlaying. This is what happens when touch
+ * is dispatched on the caption area while overlaying it on content:
+ * <ul>
+ * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the
+ * down action is performed on top close or maximize buttons; the reason for that is we want these
+ * buttons to always work.</li>
+ * <li>The content View will receive the touch event. Mind that content is actually underneath the
+ * caption, so we need to introduce our own dispatch ordering. We achieve this by overriding
+ * {@link #buildTouchDispatchChildList()}.</li>
+ * <li>If the touch event is not consumed by the content View, it will go to the caption View
+ * and the dragging logic will be executed.</li>
+ * </ul>
+ */
+public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
+ GestureDetector.OnGestureListener {
+ private final static String TAG = "DecorCaptionView";
+ private PhoneWindow mOwner = null;
+ private boolean mShow = false;
+
+ // True if the window is being dragged.
+ private boolean mDragging = false;
+
+ // True when the left mouse button got released while dragging.
+ private boolean mLeftMouseButtonReleased;
+
+ private boolean mOverlayWithAppContent = false;
+
+ private View mCaption;
+ private View mContent;
+ private View mMaximize;
+ private View mClose;
+
+ // Fields for detecting drag events.
+ private int mTouchDownX;
+ private int mTouchDownY;
+ private boolean mCheckForDragging;
+ private int mDragSlop;
+
+ // Fields for detecting and intercepting click events on close/maximize.
+ private ArrayList<View> mTouchDispatchList = new ArrayList<>(2);
+ // We use the gesture detector to detect clicks on close/maximize buttons and to be consistent
+ // with existing click detection.
+ private GestureDetector mGestureDetector;
+ private final Rect mCloseRect = new Rect();
+ private final Rect mMaximizeRect = new Rect();
+ private View mClickTarget;
+
+ public DecorCaptionView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public DecorCaptionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public DecorCaptionView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ mDragSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mGestureDetector = new GestureDetector(context, this);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCaption = getChildAt(0);
+ }
+
+ public void setPhoneWindow(PhoneWindow owner, boolean show) {
+ mOwner = owner;
+ mShow = show;
+ mOverlayWithAppContent = owner.getOverlayDecorCaption();
+ if (mOverlayWithAppContent) {
+ // The caption is covering the content, so we make its background transparent to make
+ // the content visible.
+ mCaption.setBackgroundColor(Color.TRANSPARENT);
+ }
+ updateCaptionVisibility();
+ // By changing the outline provider to BOUNDS, the window can remove its
+ // background without removing the shadow.
+ mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
+ mMaximize = findViewById(R.id.maximize_window);
+ mClose = findViewById(R.id.close_window);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // If the user starts touch on the maximize/close buttons, we immediately intercept, so
+ // that these buttons are always clickable.
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ if (mMaximizeRect.contains(x, y)) {
+ mClickTarget = mMaximize;
+ }
+ if (mCloseRect.contains(x, y)) {
+ mClickTarget = mClose;
+ }
+ }
+ return mClickTarget != null;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mClickTarget != null) {
+ mGestureDetector.onTouchEvent(event);
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mClickTarget = null;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent e) {
+ // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
+ // the old input device events get cancelled first. So no need to remember the kind of
+ // input device we are listening to.
+ final int x = (int) e.getX();
+ final int y = (int) e.getY();
+ switch (e.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (!mShow) {
+ // When there is no caption we should not react to anything.
+ return false;
+ }
+ // Checking for a drag action is started if we aren't dragging already and the
+ // starting event is either a left mouse button or any other input device.
+ if (((e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
+ (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0))) {
+ mCheckForDragging = true;
+ mTouchDownX = x;
+ mTouchDownY = y;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (!mDragging && mCheckForDragging && passedSlop(x, y)) {
+ mCheckForDragging = false;
+ mDragging = true;
+ mLeftMouseButtonReleased = false;
+ startMovingTask(e.getRawX(), e.getRawY());
+ } else if (mDragging && !mLeftMouseButtonReleased) {
+ if (e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE &&
+ (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) == 0) {
+ // There is no separate mouse button up call and if the user mixes mouse
+ // button drag actions, we stop dragging once he releases the button.
+ mLeftMouseButtonReleased = true;
+ break;
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (!mDragging) {
+ break;
+ }
+ // Abort the ongoing dragging.
+ mDragging = false;
+ return !mCheckForDragging;
+ }
+ return mDragging || mCheckForDragging;
+ }
+
+ @Override
+ public ArrayList<View> buildTouchDispatchChildList() {
+ mTouchDispatchList.ensureCapacity(3);
+ if (mCaption != null) {
+ mTouchDispatchList.add(mCaption);
+ }
+ if (mContent != null) {
+ mTouchDispatchList.add(mContent);
+ }
+ return mTouchDispatchList;
+ }
+
+ private boolean passedSlop(int x, int y) {
+ return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop;
+ }
+
+ /**
+ * The phone window configuration has changed and the caption needs to be updated.
+ * @param show True if the caption should be shown.
+ */
+ public void onConfigurationChanged(boolean show) {
+ mShow = show;
+ updateCaptionVisibility();
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ if (!(params instanceof MarginLayoutParams)) {
+ throw new IllegalArgumentException(
+ "params " + params + " must subclass MarginLayoutParams");
+ }
+ // Make sure that we never get more then one client area in our view.
+ if (index >= 2 || getChildCount() >= 2) {
+ throw new IllegalStateException("DecorCaptionView can only handle 1 client view");
+ }
+ // To support the overlaying content in the caption, we need to put the content view as the
+ // first child to get the right Z-Ordering.
+ super.addView(child, 0, params);
+ mContent = child;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int captionHeight;
+ if (mCaption.getVisibility() != View.GONE) {
+ measureChildWithMargins(mCaption, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ captionHeight = mCaption.getMeasuredHeight();
+ } else {
+ captionHeight = 0;
+ }
+ if (mContent != null) {
+ if (mOverlayWithAppContent) {
+ measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec,
+ captionHeight);
+ }
+ }
+
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int captionHeight;
+ if (mCaption.getVisibility() != View.GONE) {
+ mCaption.layout(0, 0, mCaption.getMeasuredWidth(), mCaption.getMeasuredHeight());
+ captionHeight = mCaption.getBottom() - mCaption.getTop();
+ mMaximize.getHitRect(mMaximizeRect);
+ mClose.getHitRect(mCloseRect);
+ } else {
+ captionHeight = 0;
+ mMaximizeRect.setEmpty();
+ mCloseRect.setEmpty();
+ }
+
+ if (mContent != null) {
+ if (mOverlayWithAppContent) {
+ mContent.layout(0, 0, mContent.getMeasuredWidth(), mContent.getMeasuredHeight());
+ } else {
+ mContent.layout(0, captionHeight, mContent.getMeasuredWidth(),
+ captionHeight + mContent.getMeasuredHeight());
+ }
+ }
+ }
+ /**
+ * Determine if the workspace is entirely covered by the window.
+ * @return Returns true when the window is filling the entire screen/workspace.
+ **/
+ private boolean isFillingScreen() {
+ return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
+ (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
+ }
+
+ /**
+ * Updates the visibility of the caption.
+ **/
+ private void updateCaptionVisibility() {
+ // Don't show the caption if the window has e.g. entered full screen.
+ boolean invisible = isFillingScreen() || !mShow;
+ mCaption.setVisibility(invisible ? GONE : VISIBLE);
+ mCaption.setOnTouchListener(this);
+ }
+
+ /**
+ * Maximize the window by moving it to the maximized workspace stack.
+ **/
+ private void maximizeWindow() {
+ Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+ if (callback != null) {
+ try {
+ callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Cannot change task workspace.");
+ }
+ }
+ }
+
+ public boolean isCaptionShowing() {
+ return mShow;
+ }
+
+ public int getCaptionHeight() {
+ return (mCaption != null) ? mCaption.getHeight() : 0;
+ }
+
+ public void removeContentView() {
+ if (mContent != null) {
+ removeView(mContent);
+ mContent = null;
+ }
+ }
+
+ public View getCaption() {
+ return mCaption;
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MarginLayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new MarginLayoutParams(MarginLayoutParams.MATCH_PARENT,
+ MarginLayoutParams.MATCH_PARENT);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(LayoutParams p) {
+ return new MarginLayoutParams(p);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof MarginLayoutParams;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ if (mClickTarget == mMaximize) {
+ maximizeWindow();
+ } else if (mClickTarget == mClose) {
+ mOwner.dispatchOnWindowDismissed(true /*finishTask*/);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 60ef4a4..889c7b3 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -126,6 +126,8 @@
private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+ private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info";
+
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
@@ -578,6 +580,29 @@
}
/**
+ * Sets the device owner information. If the information is {@code null} or empty then the
+ * device owner info is cleared.
+ *
+ * @param info Device owner information which will be displayed instead of the user
+ * owner info.
+ */
+ public void setDeviceOwnerInfo(String info) {
+ if (info != null && info.isEmpty()) {
+ info = null;
+ }
+
+ setString(LOCK_SCREEN_DEVICE_OWNER_INFO, info, UserHandle.USER_SYSTEM);
+ }
+
+ public String getDeviceOwnerInfo() {
+ return getString(LOCK_SCREEN_DEVICE_OWNER_INFO, UserHandle.USER_SYSTEM);
+ }
+
+ public boolean isDeviceOwnerInfoEnabled() {
+ return getDeviceOwnerInfo() != null;
+ }
+
+ /**
* Compute the password quality from the given password string.
*/
static public int computePasswordQuality(String password) {
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
deleted file mode 100644
index de6e228..0000000
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ /dev/null
@@ -1,668 +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.internal.widget;
-
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.AttributeSet;
-import android.view.Choreographer;
-import android.view.DisplayListCanvas;
-import android.view.MotionEvent;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.Window;
-import android.view.WindowCallbacks;
-import android.util.Log;
-import android.util.TypedValue;
-
-import com.android.internal.R;
-import com.android.internal.policy.PhoneWindow;
-
-/**
- * This class represents the special screen elements to control a window on free form
- * environment. All thse screen elements are added in the "non client area" which is the area of
- * the window which is handled by the OS and not the application.
- * As such this class handles the following things:
- * <ul>
- * <li>The caption, containing the system buttons like maximize, close and such as well as
- * allowing the user to drag the window around.</li>
- * <li>The shadow - which is changing dependent on the window focus.</li>
- * <li>The border around the client area (if there is one).</li>
- * <li>The resize handles which allow to resize the window.</li>
- * </ul>
- * After creating the view, the function
- * {@link #setPhoneWindow} needs to be called to make
- * the connection to it's owning PhoneWindow.
- * Note: At this time the application can change various attributes of the DecorView which
- * will break things (in settle/unexpected ways):
- * <ul>
- * <li>setElevation</li>
- * <li>setOutlineProvider</li>
- * <li>setSurfaceFormat</li>
- * <li>..</li>
- * </ul>
- * This will be mitigated once b/22527834 will be addressed.
- */
-public class NonClientDecorView extends LinearLayout
- implements View.OnClickListener, View.OnTouchListener, WindowCallbacks {
- private final static String TAG = "NonClientDecorView";
- // The height of a window which has focus in DIP.
- private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
- // The height of a window which has not in DIP.
- private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
- private PhoneWindow mOwner = null;
- private boolean mWindowHasShadow = false;
- private boolean mShowDecor = false;
- // True when this object is listening for window size changes.
- private boolean mAttachedCallbacksToRootViewImpl = false;
-
- // True if the window is being dragged.
- private boolean mDragging = false;
-
- // True when the left mouse button got released while dragging.
- private boolean mLeftMouseButtonReleased;
-
- // True if this window is resizable (which is currently only true when the decor is shown).
- public boolean mVisible = false;
-
- // The current focus state of the window for updating the window elevation.
- private boolean mWindowHasFocus = true;
-
- // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
- // size calculation takes the shadow size into account. We set the elevation currently
- // to max until the first layout command has been executed.
- private boolean mAllowUpdateElevation = false;
-
- // The resize frame renderer.
- private ResizeFrameThread mFrameRendererThread = null;
-
- private Drawable mResizingBackgroundDrawable;
- private Drawable mCaptionBackgroundDrawable;
-
- public NonClientDecorView(Context context) {
- super(context);
- }
-
- public NonClientDecorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public NonClientDecorView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (!mAttachedCallbacksToRootViewImpl) {
- // If there is no window callback installed there was no window set before. Set it now.
- // Note that our ViewRootImpl object will not change.
- getViewRootImpl().addWindowCallbacks(this);
- mAttachedCallbacksToRootViewImpl = true;
- } else if (mFrameRendererThread != null) {
- // We are resizing and this call happened due to a configuration change. Tell the
- // renderer about it.
- mFrameRendererThread.onConfigurationChange();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mAttachedCallbacksToRootViewImpl) {
- getViewRootImpl().removeWindowCallbacks(this);
- mAttachedCallbacksToRootViewImpl = false;
- }
- }
-
- public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow,
- Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawableDrawable) {
- mOwner = owner;
- mWindowHasShadow = windowHasShadow;
- mShowDecor = showDecor;
- mResizingBackgroundDrawable = resizingBackgroundDrawable;
- mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
- updateCaptionVisibility();
- if (mWindowHasShadow) {
- initializeElevation();
- }
- // By changing the outline provider to BOUNDS, the window can remove its
- // background without removing the shadow.
- mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
-
- findViewById(R.id.maximize_window).setOnClickListener(this);
- findViewById(R.id.close_window).setOnClickListener(this);
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent e) {
- // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
- // the old input device events get cancelled first. So no need to remember the kind of
- // input device we are listening to.
- switch (e.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (!mShowDecor) {
- // When there is no decor we should not react to anything.
- return false;
- }
- // A drag action is started if we aren't dragging already and the starting event is
- // either a left mouse button or any other input device.
- if (!mDragging &&
- (e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
- (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0)) {
- mDragging = true;
- mLeftMouseButtonReleased = false;
- startMovingTask(e.getRawX(), e.getRawY());
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (mDragging && !mLeftMouseButtonReleased) {
- if (e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE &&
- (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) == 0) {
- // There is no separate mouse button up call and if the user mixes mouse
- // button drag actions, we stop dragging once he releases the button.
- mLeftMouseButtonReleased = true;
- break;
- }
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mDragging) {
- break;
- }
- // Abort the ongoing dragging.
- mDragging = false;
- return true;
- }
- return mDragging;
- }
-
- /**
- * The phone window configuration has changed and the decor needs to be updated.
- * @param showDecor True if the decor should be shown.
- * @param windowHasShadow True when the window should show a shadow.
- **/
- public void phoneWindowUpdated(boolean showDecor, boolean windowHasShadow) {
- mShowDecor = showDecor;
- updateCaptionVisibility();
- if (windowHasShadow != mWindowHasShadow) {
- mWindowHasShadow = windowHasShadow;
- initializeElevation();
- }
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.maximize_window) {
- maximizeWindow();
- } else if (view.getId() == R.id.close_window) {
- mOwner.dispatchOnWindowDismissed(true /*finishTask*/);
- }
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- mWindowHasFocus = hasWindowFocus;
- updateElevation();
- super.onWindowFocusChanged(hasWindowFocus);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // If the application changed its SystemUI metrics, we might also have to adapt
- // our shadow elevation.
- updateElevation();
- mAllowUpdateElevation = true;
-
- super.onLayout(changed, left, top, right, bottom);
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- // Make sure that we never get more then one client area in our view.
- if (index >= 2 || getChildCount() >= 2) {
- throw new IllegalStateException("NonClientDecorView can only handle 1 client view");
- }
- super.addView(child, index, params);
- }
-
- /**
- * Determine if the workspace is entirely covered by the window.
- * @return Returns true when the window is filling the entire screen/workspace.
- **/
- private boolean isFillingScreen() {
- return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
- (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
- }
-
- /**
- * Updates the visibility of the caption.
- **/
- private void updateCaptionVisibility() {
- // Don't show the decor if the window has e.g. entered full screen.
- boolean invisible = isFillingScreen() || !mShowDecor;
- View caption = getChildAt(0);
- caption.setVisibility(invisible ? GONE : VISIBLE);
- caption.setOnTouchListener(this);
- mVisible = !invisible;
- }
-
- /**
- * The elevation gets set for the first time and the framework needs to be informed that
- * the surface layer gets created with the shadow size in mind.
- **/
- private void initializeElevation() {
- // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
- mAllowUpdateElevation = false;
- if (mWindowHasShadow) {
- updateElevation();
- } else {
- mOwner.setElevation(0);
- }
- }
-
- /**
- * The shadow height gets controlled by the focus to visualize highlighted windows.
- * Note: This will overwrite application elevation properties.
- * Note: Windows which have (temporarily) changed their attributes to cover the SystemUI
- * will get no shadow as they are expected to be "full screen".
- **/
- private void updateElevation() {
- float elevation = 0;
- // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
- // is bound to the content size and not the target size.
- if (mWindowHasShadow && mFrameRendererThread == null) {
- boolean fill = isFillingScreen();
- elevation = fill ? 0 :
- (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
- DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
- // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
- if (!mAllowUpdateElevation && !fill) {
- elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
- }
- // Convert the DP elevation into physical pixels.
- elevation = dipToPx(elevation);
- }
- // Don't change the elevation if it didn't change since it can require some time.
- if (mOwner.getDecorView().getElevation() != elevation) {
- mOwner.setElevation(elevation);
- }
- }
-
- /**
- * Converts a DIP measure into physical pixels.
- * @param dip The dip value.
- * @return Returns the number of pixels.
- */
- private float dipToPx(float dip) {
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
- getResources().getDisplayMetrics());
- }
-
- /**
- * Maximize the window by moving it to the maximized workspace stack.
- **/
- private void maximizeWindow() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
- } catch (RemoteException ex) {
- Log.e(TAG, "Cannot change task workspace.");
- }
- }
- }
-
- @Override
- public void onWindowDragResizeStart(Rect initialBounds) {
- if (mOwner.isDestroyed()) {
- // If the owner's window is gone, we should not be able to come here anymore.
- releaseResources();
- return;
- }
- if (mFrameRendererThread != null) {
- return;
- }
- final ThreadedRenderer renderer =
- (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
- if (renderer != null) {
- mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds);
- // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
- // If we want to get the shadow shown while resizing, we would need to elevate a new
- // element which owns the caption and has the elevation.
- updateElevation();
- }
- }
-
- @Override
- public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
- if (mFrameRendererThread == null) {
- return false;
- }
- return mFrameRendererThread.onContentDrawn(xOffset, yOffset, xSize, ySize);
- }
-
- @Override
- public void onRequestDraw(boolean reportNextDraw) {
- if (mFrameRendererThread != null) {
- mFrameRendererThread.onRequestDraw(reportNextDraw);
- } else if (reportNextDraw) {
- // If render thread is gone, just report immediately.
- if (isAttachedToWindow()) {
- getViewRootImpl().reportDrawFinish();
- }
- }
- }
-
- @Override
- public void onWindowDragResizeEnd() {
- releaseThreadedRenderer();
- }
-
- @Override
- public void onWindowSizeIsChanging(Rect newBounds) {
- if (mFrameRendererThread != null) {
- mFrameRendererThread.setTargetRect(newBounds);
- }
- }
-
- /**
- * Release the renderer thread which is usually done when the user stops resizing.
- */
- private void releaseThreadedRenderer() {
- if (mFrameRendererThread != null) {
- mFrameRendererThread.releaseRenderer();
- mFrameRendererThread = null;
- // Bring the shadow back.
- updateElevation();
- }
- }
-
- /**
- * Called when the parent window is destroyed to release all resources. Note that this will also
- * destroy the renderer thread.
- */
- private void releaseResources() {
- releaseThreadedRenderer();
- }
-
- /**
- * The thread which draws the chrome while we are resizing.
- * It starts with the creation and it ends once someone calls destroy().
- * Any size changes can be passed by a call to setTargetRect will passed to the thread and
- * executed via the Choreographer.
- * TODO(b/24810450): Separate functionality from non-client-decor so that it can be used
- * independently.
- */
- private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
- // This is containing the last requested size by a resize command. Note that this size might
- // or might not have been applied to the output already.
- private final Rect mTargetRect = new Rect();
-
- // The render nodes for the multi threaded renderer.
- private ThreadedRenderer mRenderer;
- private RenderNode mFrameAndBackdropNode;
-
- private final Rect mOldTargetRect = new Rect();
- private final Rect mNewTargetRect = new Rect();
- private Choreographer mChoreographer;
-
- // Cached size values from the last render for the case that the view hierarchy is gone
- // during a configuration change.
- private int mLastContentWidth;
- private int mLastContentHeight;
- private int mLastCaptionHeight;
- private int mLastXOffset;
- private int mLastYOffset;
-
- // Whether to report when next frame is drawn or not.
- private boolean mReportNextDraw;
-
- ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
- setName("ResizeFrame");
- mRenderer = renderer;
-
- // Create a render node for the content and frame backdrop
- // which can be resized independently from the content.
- mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
-
- mRenderer.addRenderNode(mFrameAndBackdropNode, true);
-
- // Set the initial bounds and draw once so that we do not get a broken frame.
- mTargetRect.set(initialBounds);
- synchronized (this) {
- changeWindowSizeLocked(initialBounds);
- }
-
- // Kick off our draw thread.
- start();
- }
-
- /**
- * Call this function asynchronously when the window size has been changed. The change will
- * be picked up once per frame and the frame will be re-rendered accordingly.
- * @param newTargetBounds The new target bounds.
- */
- public void setTargetRect(Rect newTargetBounds) {
- synchronized (this) {
- mTargetRect.set(newTargetBounds);
- // Notify of a bounds change.
- pingRenderLocked();
- }
- }
-
- /**
- * The window got replaced due to a configuration change.
- */
- public void onConfigurationChange() {
- synchronized (this) {
- if (mRenderer != null) {
- // Enforce a window redraw.
- mOldTargetRect.set(0, 0, 0, 0);
- pingRenderLocked();
- }
- }
- }
-
- /**
- * All resources of the renderer will be released. This function can be called from the
- * the UI thread as well as the renderer thread.
- */
- public void releaseRenderer() {
- synchronized (this) {
- if (mRenderer != null) {
- // Invalidate the current content bounds.
- mRenderer.setContentDrawBounds(0, 0, 0, 0);
-
- // Remove the render node again
- // (see comment above - better to do that only once).
- mRenderer.removeRenderNode(mFrameAndBackdropNode);
-
- mRenderer = null;
-
- // Exit the renderer loop.
- pingRenderLocked();
- }
- }
- }
-
- @Override
- public void run() {
- try {
- Looper.prepare();
- synchronized (this) {
- mChoreographer = Choreographer.getInstance();
-
- // Draw at least once.
- mChoreographer.postFrameCallback(this);
- }
- Looper.loop();
- } finally {
- releaseRenderer();
- }
- synchronized (this) {
- // Make sure no more messages are being sent.
- mChoreographer = null;
- }
- }
-
- /**
- * The implementation of the FrameCallback.
- * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
- * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000}
- */
- @Override
- public void doFrame(long frameTimeNanos) {
- synchronized (this) {
- if (mRenderer == null) {
- reportDrawIfNeeded();
- // Tell the looper to stop. We are done.
- Looper.myLooper().quit();
- return;
- }
- mNewTargetRect.set(mTargetRect);
- if (!mNewTargetRect.equals(mOldTargetRect) || mReportNextDraw) {
- mOldTargetRect.set(mNewTargetRect);
- changeWindowSizeLocked(mNewTargetRect);
- }
- }
- }
-
- /**
- * The content is about to be drawn and we got the location of where it will be shown.
- * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
- * if the previous call was ignored since the size was unknown.
- * @param xOffset The x offset where the content is drawn to.
- * @param yOffset The y offset where the content is drawn to.
- * @param xSize The width size of the content. This should not be 0.
- * @param ySize The height of the content.
- * @return true if a frame should be requested after the content is drawn; false otherwise.
- */
- public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
- synchronized (this) {
- final boolean firstCall = mLastContentWidth == 0;
- // The current content buffer is drawn here.
- mLastContentWidth = xSize;
- mLastContentHeight = ySize - mLastCaptionHeight;
- mLastXOffset = xOffset;
- mLastYOffset = yOffset;
-
- mRenderer.setContentDrawBounds(
- mLastXOffset,
- mLastYOffset,
- mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
- // If this was the first call and changeWindowSizeLocked got already called prior
- // to us, we should re-issue a changeWindowSizeLocked now.
- return firstCall && (mLastCaptionHeight != 0 || !mShowDecor);
- }
- }
-
- public void onRequestDraw(boolean reportNextDraw) {
- synchronized (this) {
- mReportNextDraw = reportNextDraw;
- mOldTargetRect.set(0, 0, 0, 0);
- pingRenderLocked();
- }
- }
-
- /**
- * Resizing the frame to fit the new window size.
- * @param newBounds The window bounds which needs to be drawn.
- */
- private void changeWindowSizeLocked(Rect newBounds) {
- // While a configuration change is taking place the view hierarchy might become
- // inaccessible. For that case we remember the previous metrics to avoid flashes.
- // Note that even when there is no visible caption, the caption child will exist.
- View caption = getChildAt(0);
- if (caption != null) {
- final int captionHeight = caption.getHeight();
- // The caption height will probably never dynamically change while we are resizing.
- // Once set to something other then 0 it should be kept that way.
- if (captionHeight != 0) {
- // Remember the height of the caption.
- mLastCaptionHeight = captionHeight;
- }
- }
- // Make sure that the other thread has already prepared the render draw calls for the
- // content. If any size is 0, we have to wait for it to be drawn first.
- if ((mLastCaptionHeight == 0 && mShowDecor) ||
- mLastContentWidth == 0 || mLastContentHeight == 0) {
- return;
- }
- // Since the surface is spanning the entire screen, we have to add the start offset of
- // the bounds to get to the surface location.
- final int left = mLastXOffset + newBounds.left;
- final int top = mLastYOffset + newBounds.top;
- final int width = newBounds.width();
- final int height = newBounds.height();
-
- mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
-
- // Draw the caption and content backdrops in to our render node.
- DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
- mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
- mCaptionBackgroundDrawable.draw(canvas);
-
- // The backdrop: clear everything with the background. Clipping is done elsewhere.
- mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
- mResizingBackgroundDrawable.draw(canvas);
- mFrameAndBackdropNode.end(canvas);
-
- // We need to render the node explicitly
- mRenderer.drawRenderNode(mFrameAndBackdropNode);
-
- reportDrawIfNeeded();
- }
-
- /**
- * Notify view root that a frame has been drawn by us, if it has requested so.
- */
- private void reportDrawIfNeeded() {
- if (mReportNextDraw) {
- if (isAttachedToWindow()) {
- getViewRootImpl().reportDrawFinish();
- }
- mReportNextDraw = false;
- }
- }
-
- /**
- * Sends a message to the renderer to wake up and perform the next action which can be
- * either the next rendering or the self destruction if mRenderer is null.
- * Note: This call must be synchronized.
- */
- private void pingRenderLocked() {
- if (mChoreographer != null) {
- mChoreographer.postFrameCallback(this);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 6217d6e..948a6bb 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -1095,7 +1095,16 @@
View child = getChildAt(i);
ii = infoForChild(child);
if (ii != null && ii.position == mCurItem) {
- if (child.requestFocus(focusDirection)) {
+ final Rect focusRect;
+ if (currentFocused == null) {
+ focusRect = null;
+ } else {
+ focusRect = mTempRect;
+ currentFocused.getFocusedRect(mTempRect);
+ offsetDescendantRectToMyCoords(currentFocused, mTempRect);
+ offsetRectIntoDescendantCoords(child, mTempRect);
+ }
+ if (child.requestFocus(focusDirection, focusRect)) {
break;
}
}
@@ -1321,6 +1330,11 @@
}
}
+ public Object getCurrent() {
+ final ItemInfo itemInfo = infoForPosition(getCurrentItem());
+ return itemInfo == null ? null : itemInfo.object;
+ }
+
@Override
public void removeView(View view) {
if (mInLayout) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ecee2b2..4d648ce 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -89,6 +89,7 @@
android_util_Binder.cpp \
android_util_EventLog.cpp \
android_util_Log.cpp \
+ android_util_PathParser.cpp \
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ffc69a9..bd41c5d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -110,6 +110,7 @@
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
+extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
@@ -579,7 +580,8 @@
char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
char usejitOptsBuf[sizeof("-Xusejit:")-1 + PROPERTY_VALUE_MAX];
- char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
+ char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
+ char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
@@ -607,15 +609,6 @@
kEMIntFast,
kEMJitCompiler,
} executionMode = kEMDefault;
- char profilePeriod[sizeof("-Xprofile-period:")-1 + PROPERTY_VALUE_MAX];
- char profileDuration[sizeof("-Xprofile-duration:")-1 + PROPERTY_VALUE_MAX];
- char profileInterval[sizeof("-Xprofile-interval:")-1 + PROPERTY_VALUE_MAX];
- char profileBackoff[sizeof("-Xprofile-backoff:")-1 + PROPERTY_VALUE_MAX];
- char profileTopKThreshold[sizeof("-Xprofile-top-k-threshold:")-1 + PROPERTY_VALUE_MAX];
- char profileTopKChangeThreshold[sizeof("-Xprofile-top-k-change-threshold:")-1 +
- PROPERTY_VALUE_MAX];
- char profileType[sizeof("-Xprofile-type:")-1 + PROPERTY_VALUE_MAX];
- char profileMaxStackDepth[sizeof("-Xprofile-max-stack-depth:")-1 + PROPERTY_VALUE_MAX];
char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
@@ -692,7 +685,8 @@
* JIT related options.
*/
parseRuntimeOption("dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:");
- parseRuntimeOption("dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
+ parseRuntimeOption("dalvik.vm.jitmaxsize", jitmaxsizeOptsBuf, "-Xjitmaxsize:");
+ parseRuntimeOption("dalvik.vm.jitinitialsize", jitinitialsizeOptsBuf, "-Xjitinitialsize:");
parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");
property_get("ro.config.low_ram", propBuf, "");
@@ -834,75 +828,22 @@
addOption(localeOption);
}
- /*
- * Set profiler options
- */
- // Whether or not the profiler should be enabled.
- property_get("dalvik.vm.profiler", propBuf, "0");
- if (propBuf[0] == '1') {
- addOption("-Xenable-profiler");
- }
-
- // Whether the profile should start upon app startup or be delayed by some random offset
- // (in seconds) that is bound between 0 and a fixed value.
- property_get("dalvik.vm.profile.start-immed", propBuf, "0");
- if (propBuf[0] == '1') {
- addOption("-Xprofile-start-immediately");
- }
-
- // Number of seconds during profile runs.
- parseRuntimeOption("dalvik.vm.profile.period-secs", profilePeriod, "-Xprofile-period:");
-
- // Length of each profile run (seconds).
- parseRuntimeOption("dalvik.vm.profile.duration-secs",
- profileDuration,
- "-Xprofile-duration:");
-
- // Polling interval during profile run (microseconds).
- parseRuntimeOption("dalvik.vm.profile.interval-us", profileInterval, "-Xprofile-interval:");
-
- // Coefficient for period backoff. The the period is multiplied
- // by this value after each profile run.
- parseRuntimeOption("dalvik.vm.profile.backoff-coeff", profileBackoff, "-Xprofile-backoff:");
-
- // Top K% of samples that are considered relevant when
- // deciding if the app should be recompiled.
- parseRuntimeOption("dalvik.vm.profile.top-k-thr",
- profileTopKThreshold,
- "-Xprofile-top-k-threshold:");
-
- // The threshold after which a change in the structure of the
- // top K% profiled samples becomes significant and triggers
- // recompilation. A change in profile is considered
- // significant if X% (top-k-change-threshold) of the top K%
- // (top-k-threshold property) samples has changed.
- parseRuntimeOption("dalvik.vm.profile.top-k-ch-thr",
- profileTopKChangeThreshold,
- "-Xprofile-top-k-change-threshold:");
-
- // Type of profile data.
- parseRuntimeOption("dalvik.vm.profiler.type", profileType, "-Xprofile-type:");
-
- // Depth of bounded stack data
- parseRuntimeOption("dalvik.vm.profile.stack-depth",
- profileMaxStackDepth,
- "-Xprofile-max-stack-depth:");
-
- /*
- * Tracing options.
- */
- property_get("dalvik.vm.method-trace", propBuf, "false");
- if (strcmp(propBuf, "true") == 0) {
- addOption("-Xmethod-trace");
- parseRuntimeOption("dalvik.vm.method-trace-file",
- methodTraceFileBuf,
- "-Xmethod-trace-file:");
- parseRuntimeOption("dalvik.vm.method-trace-file-siz",
- methodTraceFileSizeBuf,
- "-Xmethod-trace-file-size:");
- property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+ // Trace files are stored in /data/misc/trace which is writable only in debug mode.
+ property_get("ro.debuggable", propBuf, "0");
+ if (strcmp(propBuf, "1") == 0) {
+ property_get("dalvik.vm.method-trace", propBuf, "false");
if (strcmp(propBuf, "true") == 0) {
- addOption("-Xmethod-trace-stream");
+ addOption("-Xmethod-trace");
+ parseRuntimeOption("dalvik.vm.method-trace-file",
+ methodTraceFileBuf,
+ "-Xmethod-trace-file:");
+ parseRuntimeOption("dalvik.vm.method-trace-file-siz",
+ methodTraceFileSizeBuf,
+ "-Xmethod-trace-file-size:");
+ property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+ if (strcmp(propBuf, "true") == 0) {
+ addOption("-Xmethod-trace-stream");
+ }
}
}
@@ -1299,6 +1240,7 @@
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
+ REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 703a9bd..a805b6d 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1006,6 +1006,7 @@
// is disposed.
int dupFd = dup(blob.fd());
if (dupFd < 0) {
+ ALOGE("Error allocating dup fd. Error:%d", errno);
blob.release();
SkSafeUnref(ctable);
doThrowRE(env, "Could not allocate dup blob fd.");
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index 7fd288a..9b774b3 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -63,6 +63,11 @@
layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
}
+bool MinikinUtils::hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs) {
+ const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
+ return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
+}
+
float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) {
switch (paint->getTextAlign()) {
case Paint::kCenter_Align:
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index 1ee6245..5bf1eec 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -40,6 +40,8 @@
TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
size_t bufSize);
+ static bool hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs);
+
static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path);
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index b50046f..9c11dd1 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -39,6 +39,7 @@
#include <minikin/GraphemeBreak.h>
#include <minikin/Measurement.h>
+#include <unicode/utf16.h>
#include "MinikinSkia.h"
#include "MinikinUtils.h"
#include "Paint.h"
@@ -852,45 +853,44 @@
return false;
}
- static jboolean hasGlyphVariation(const Paint* paint, TypefaceImpl* typeface, jint bidiFlags,
- const jchar* chars, size_t size) {
- // TODO: query font for whether character has variation selector; requires a corresponding
- // function in Minikin.
- return false;
- }
-
static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
jint bidiFlags, jstring string) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
ScopedStringChars str(env, string);
- /* start by rejecting variation selectors (not supported yet) */
+ /* Start by rejecting unsupported base code point and variation selector pairs. */
size_t nChars = 0;
+ const uint32_t kStartOfString = 0xFFFFFFFF;
+ uint32_t prevCp = kStartOfString;
for (size_t i = 0; i < str.size(); i++) {
- jchar c = str[i];
- if (0xDC00 <= c && c <= 0xDFFF) {
+ jchar cu = str[i];
+ uint32_t cp = cu;
+ if (U16_IS_TRAIL(cu)) {
// invalid UTF-16, unpaired trailing surrogate
return false;
- } else if (0xD800 <= c && c <= 0xDBFF) {
+ } else if (U16_IS_LEAD(cu)) {
if (i + 1 == str.size()) {
// invalid UTF-16, unpaired leading surrogate at end of string
return false;
}
i++;
- jchar c2 = str[i];
- if (!(0xDC00 <= c2 && c2 <= 0xDFFF)) {
+ jchar cu2 = str[i];
+ if (!U16_IS_TRAIL(cu2)) {
// invalid UTF-16, unpaired leading surrogate
return false;
}
- // UTF-16 encoding of range U+E0100..U+E01EF is DB40 DD00 .. DB40 DDEF
- if (c == 0xDB40 && 0xDD00 <= c2 && c2 <= 0xDDEF) {
- return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
- }
- } else if (0xFE00 <= c && c <= 0xFE0F) {
- return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
+ cp = U16_GET_SUPPLEMENTARY(cu, cu2);
+ }
+
+ if (prevCp != kStartOfString &&
+ ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF)) &&
+ !MinikinUtils::hasVariationSelector(typeface, prevCp, cp)) {
+ // No font has a glyph for the code point and variation selector pair.
+ return false;
}
nChars++;
+ prevCp = cp;
}
Layout layout;
MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(),
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 3d96fab..e4e73a4 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -544,39 +544,6 @@
float totalAdvance;
};
-// Same values used by Skia
-#define kStdStrikeThru_Offset (-6.0f / 21.0f)
-#define kStdUnderline_Offset (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
- uint32_t flags;
- SkDrawFilter* drawFilter = canvas->getDrawFilter();
- if (drawFilter) {
- SkPaint paintCopy(paint);
- drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
- flags = paintCopy.getFlags();
- } else {
- flags = paint.getFlags();
- }
- if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
- SkScalar left = x;
- SkScalar right = x + length;
- float textSize = paint.getTextSize();
- float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
- if (flags & SkPaint::kUnderlineText_Flag) {
- SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
- canvas->drawRect(left, top, right, bottom, paint);
- }
- if (flags & SkPaint::kStrikeThruText_Flag) {
- SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
- SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
- canvas->drawRect(left, top, right, bottom, paint);
- }
- }
-}
-
void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
// minikin may modify the original paint
@@ -586,8 +553,8 @@
MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
size_t nGlyphs = layout.nGlyphs();
- uint16_t* glyphs = new uint16_t[nGlyphs];
- float* pos = new float[nGlyphs * 2];
+ std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
+ std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
@@ -597,13 +564,9 @@
bounds.offset(x, y);
}
- DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
+ DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(),
+ paint, x, y, bounds, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
-
- drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
-
- delete[] glyphs;
- delete[] pos;
}
static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
new file mode 100644
index 0000000..0927120
--- /dev/null
+++ b/core/jni/android_util_PathParser.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+#include <PathParser.h>
+#include <SkPath.h>
+#include <utils/VectorDrawableUtils.h>
+
+#include <android/log.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+ jint strLength) {
+ const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
+ SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
+
+ PathParser::ParseResult result;
+ PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
+ env->ReleaseStringUTFChars(inputPathStr, pathString);
+ if (result.failureOccurred) {
+ ALOGE(result.failureMessage.c_str());
+ }
+ return !result.failureOccurred;
+}
+
+static long createEmptyPathData(JNIEnv*, jobject) {
+ PathData* pathData = new PathData();
+ return reinterpret_cast<jlong>(pathData);
+}
+
+static long createPathData(JNIEnv*, jobject, jlong pathDataPtr) {
+ PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+ PathData* newPathData = new PathData(*pathData);
+ return reinterpret_cast<jlong>(newPathData);
+}
+
+static long createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr, jint strLength) {
+ const char* pathString = env->GetStringUTFChars(inputStr, NULL);
+ PathData* pathData = new PathData();
+ PathParser::ParseResult result;
+ PathParser::getPathDataFromString(pathData, &result, pathString, strLength);
+ env->ReleaseStringUTFChars(inputStr, pathString);
+ if (!result.failureOccurred) {
+ return reinterpret_cast<jlong>(pathData);
+ } else {
+ delete pathData;
+ ALOGE(result.failureMessage.c_str());
+ return NULL;
+ }
+}
+
+static bool interpolatePathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr,
+ jlong toPathDataPtr, jfloat fraction) {
+ PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr);
+ PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+ PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr);
+ return VectorDrawableUtils::interpolatePathData(outPathData, *fromPathData,
+ *toPathData, fraction);
+}
+
+static void deletePathData(JNIEnv*, jobject, jlong pathDataHandle) {
+ PathData* pathData = reinterpret_cast<PathData*>(pathDataHandle);
+ delete pathData;
+}
+
+static bool canMorphPathData(JNIEnv*, jobject, jlong fromPathDataPtr, jlong toPathDataPtr) {
+ PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+ PathData* toPathData = reinterpret_cast<PathData*>(toPathDataPtr);
+ return VectorDrawableUtils::canMorph(*fromPathData, *toPathData);
+}
+
+static void setPathData(JNIEnv*, jobject, jlong outPathDataPtr, jlong fromPathDataPtr) {
+ PathData* fromPathData = reinterpret_cast<PathData*>(fromPathDataPtr);
+ PathData* outPathData = reinterpret_cast<PathData*>(outPathDataPtr);
+ *outPathData = *fromPathData;
+}
+
+static void setSkPathFromPathData(JNIEnv*, jobject, jlong outPathPtr, jlong pathDataPtr) {
+ PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+ SkPath* skPath = reinterpret_cast<SkPath*>(outPathPtr);
+ VectorDrawableUtils::verbsToPath(skPath, *pathData);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath},
+ {"nCreateEmptyPathData", "!()J", (void*)createEmptyPathData},
+ {"nCreatePathData", "!(J)J", (void*)createPathData},
+ {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
+ {"nInterpolatePathData", "!(JJJF)Z", (void*)interpolatePathData},
+ {"nFinalize", "!(J)V", (void*)deletePathData},
+ {"nCanMorph", "!(JJ)Z", (void*)canMorphPathData},
+ {"nSetPathData", "!(JJ)V", (void*)setPathData},
+ {"nCreatePathFromPathData", "!(JJ)V", (void*)setSkPathFromPathData},
+};
+
+int register_android_util_PathParser(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/util/PathParser", gMethods, NELEM(gMethods));
+}
+};
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index d04adbf..d7e2c02 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -24,6 +24,7 @@
#include <android_runtime/Log.h>
#include <utils/Log.h>
#include <android/graphics/GraphicsJNI.h>
+#include "ScopedLocalRef.h"
#include "core_jni_helpers.h"
@@ -35,6 +36,8 @@
jfieldID mBitmap;
jfieldID mHotSpotX;
jfieldID mHotSpotY;
+ jfieldID mBitmapFrames;
+ jfieldID mDurationPerFrame;
jmethodID getSystemIcon;
jmethodID load;
} gPointerIconClassInfo;
@@ -84,6 +87,19 @@
env->DeleteLocalRef(bitmapObj);
}
+ ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
+ env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
+ if (bitmapFramesObj.get()) {
+ outPointerIcon->durationPerFrame = env->GetIntField(
+ loadedPointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
+ jsize size = env->GetArrayLength(bitmapFramesObj.get());
+ outPointerIcon->bitmapFrames.resize(size);
+ for (jsize i = 0; i < size; ++i) {
+ ScopedLocalRef<jobject> bitmapObj(env, env->GetObjectArrayElement(bitmapFramesObj.get(), i));
+ GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmapFrames[i]));
+ }
+ }
+
env->DeleteLocalRef(loadedPointerIconObj);
return OK;
}
@@ -121,6 +137,12 @@
gPointerIconClassInfo.mHotSpotY = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz,
"mHotSpotY", "F");
+ gPointerIconClassInfo.mBitmapFrames = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz,
+ "mBitmapFrames", "[Landroid/graphics/Bitmap;");
+
+ gPointerIconClassInfo.mDurationPerFrame = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz,
+ "mDurationPerFrame", "I");
+
gPointerIconClassInfo.getSystemIcon = GetStaticMethodIDOrDie(env, gPointerIconClassInfo.clazz,
"getSystemIcon", "(Landroid/content/Context;I)Landroid/view/PointerIcon;");
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index 86f288d..ca08085 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -19,6 +19,8 @@
#include "jni.h"
+#include <vector>
+
#include <utils/Errors.h>
#include <SkBitmap.h>
@@ -69,6 +71,8 @@
SkBitmap bitmap;
float hotSpotX;
float hotSpotY;
+ std::vector<SkBitmap> bitmapFrames;
+ int32_t durationPerFrame;
inline bool isNullIcon() {
return style == POINTER_ICON_STYLE_NULL;
@@ -79,6 +83,8 @@
bitmap.reset();
hotSpotX = 0;
hotSpotY = 0;
+ bitmapFrames.clear();
+ durationPerFrame = 0;
}
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3f1be45..73c7487 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -21,6 +21,7 @@
#include <linux/fs.h>
#include <list>
+#include <sstream>
#include <string>
#include <fcntl.h>
@@ -74,8 +75,10 @@
MOUNT_EXTERNAL_WRITE = 3,
};
-static void RuntimeAbort(JNIEnv* env) {
- env->FatalError("RuntimeAbort");
+static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
+ std::ostringstream oss;
+ oss << __FILE__ << ":" << line << ": " << msg;
+ env->FatalError(oss.str().c_str());
}
// This signal handler is for zygote mode, since the zygote must reap its children
@@ -169,12 +172,11 @@
ScopedIntArrayRO gids(env, javaGids);
if (gids.get() == NULL) {
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Getting gids int array failed");
}
int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
if (rc == -1) {
- ALOGE("setgroups failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setgroups failed");
}
}
@@ -194,8 +196,7 @@
ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
if (javaRlimit.size() != 3) {
- ALOGE("rlimits array must have a second dimension of size 3");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "rlimits array must have a second dimension of size 3");
}
rlim.rlim_cur = javaRlimit[1];
@@ -205,7 +206,7 @@
if (rc == -1) {
ALOGE("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
rlim.rlim_max);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setrlimit failed");
}
}
}
@@ -216,8 +217,7 @@
static void EnableKeepCapabilities(JNIEnv* env) {
int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
if (rc == -1) {
- ALOGE("prctl(PR_SET_KEEPCAPS) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_KEEPCAPS) failed");
}
}
@@ -229,8 +229,7 @@
ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
"your kernel is compiled with file capabilities support");
} else {
- ALOGE("prctl(PR_CAPBSET_DROP) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed");
}
}
}
@@ -251,7 +250,7 @@
if (capset(&capheader, &capdata[0]) == -1) {
ALOGE("capset(%" PRId64 ", %" PRId64 ") failed", permitted, effective);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "capset failed");
}
}
@@ -259,7 +258,7 @@
errno = -set_sched_policy(0, SP_DEFAULT);
if (errno != 0) {
ALOGE("set_sched_policy(0, SP_DEFAULT) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "set_sched_policy(0, SP_DEFAULT) failed");
}
}
@@ -370,8 +369,7 @@
jsize count = env->GetArrayLength(fdsToClose);
ScopedIntArrayRO ar(env, fdsToClose);
if (ar.get() == NULL) {
- ALOGE("Bad fd array");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Bad fd array");
}
jsize i;
int devnull;
@@ -379,13 +377,13 @@
devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
ALOGE("Failed to open /dev/null: %s", strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Failed to open /dev/null");
continue;
}
ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
if (dup2(devnull, ar[i]) < 0) {
ALOGE("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Failed dup2()");
}
close(devnull);
}
@@ -493,8 +491,7 @@
// FUSE hasn't been created yet by init.
// In either case, continue without external storage.
} else {
- ALOGE("Cannot continue without emulated storage");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage");
}
}
@@ -522,13 +519,13 @@
int rc = setresgid(gid, gid, gid);
if (rc == -1) {
ALOGE("setresgid(%d) failed: %s", gid, strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setresgid failed");
}
rc = setresuid(uid, uid, uid);
if (rc == -1) {
ALOGE("setresuid(%d) failed: %s", uid, strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setresuid failed");
}
if (NeedsNoRandomizeWorkaround()) {
@@ -550,8 +547,7 @@
se_info = new ScopedUtfChars(env, java_se_info);
se_info_c_str = se_info->c_str();
if (se_info_c_str == NULL) {
- ALOGE("se_info_c_str == NULL");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "se_info_c_str == NULL");
}
}
const char* se_name_c_str = NULL;
@@ -560,15 +556,14 @@
se_name = new ScopedUtfChars(env, java_se_name);
se_name_c_str = se_name->c_str();
if (se_name_c_str == NULL) {
- ALOGE("se_name_c_str == NULL");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "se_name_c_str == NULL");
}
}
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (rc == -1) {
ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
is_system_server, se_info_c_str, se_name_c_str);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
}
// Make it easier to debug audit logs by setting the main thread's name to the
@@ -588,8 +583,7 @@
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
is_system_server ? NULL : instructionSet);
if (env->ExceptionCheck()) {
- ALOGE("Error calling post fork hooks.");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
}
} else if (pid > 0) {
// the parent process
@@ -641,7 +635,7 @@
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
}
return pid;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 561bcbc..6338088 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1683,6 +1683,10 @@
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
android:protectionLevel="system|signature" />
+ <!-- @hide -->
+ <permission android:name="android.permission.STORAGE_INTERNAL"
+ android:protectionLevel="signature" />
+
<!-- Allows access to ASEC non-destructive API calls
@hide -->
<permission android:name="android.permission.ASEC_ACCESS"
@@ -1827,6 +1831,12 @@
<permission android:name="android.permission.STATUS_BAR_SERVICE"
android:protectionLevel="signature" />
+ <!-- Allows an application to bind to third party quick settings tiles.
+ <p>Should only be requested by the System, should be required by
+ QSTileService declarations.-->
+ <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to force a BACK operation on whatever is the
top activity.
<p>Not for use by third-party applications.
@@ -2702,7 +2712,9 @@
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android"
android:supportsRtl="true"
- android:theme="@style/Theme.Material.DayNight.DarkActionBar">
+ android:theme="@style/Theme.Material.DayNight.DarkActionBar"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.DeviceDefault.Resolver"
android:finishOnCloseSystemDialogs="true"
@@ -2829,6 +2841,17 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.SystemUserHomeActivity"
+ android:enabled="false"
+ android:process=":ui"
+ android:systemUserOnly="true"
+ android:theme="@style/Theme.Translucent.NoTitleBar">
+ <intent-filter android:priority="-100">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ </intent-filter>
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:systemUserOnly="true">
<intent-filter android:priority="1000">
diff --git a/core/res/res/drawable-mdpi/pointer_wait_0.png b/core/res/res/drawable-mdpi/pointer_wait_0.png
new file mode 100644
index 0000000..adb7806
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_1.png b/core/res/res/drawable-mdpi/pointer_wait_1.png
new file mode 100644
index 0000000..fc6b42f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_10.png b/core/res/res/drawable-mdpi/pointer_wait_10.png
new file mode 100644
index 0000000..02968b5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_10.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_11.png b/core/res/res/drawable-mdpi/pointer_wait_11.png
new file mode 100644
index 0000000..24f866b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_11.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_12.png b/core/res/res/drawable-mdpi/pointer_wait_12.png
new file mode 100644
index 0000000..d1a31bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_12.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_13.png b/core/res/res/drawable-mdpi/pointer_wait_13.png
new file mode 100644
index 0000000..b0c6798
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_13.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_14.png b/core/res/res/drawable-mdpi/pointer_wait_14.png
new file mode 100644
index 0000000..721e86d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_14.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_15.png b/core/res/res/drawable-mdpi/pointer_wait_15.png
new file mode 100644
index 0000000..adb0199
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_15.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_16.png b/core/res/res/drawable-mdpi/pointer_wait_16.png
new file mode 100644
index 0000000..3695c18
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_16.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_17.png b/core/res/res/drawable-mdpi/pointer_wait_17.png
new file mode 100644
index 0000000..861605e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_17.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_18.png b/core/res/res/drawable-mdpi/pointer_wait_18.png
new file mode 100644
index 0000000..f5dfdcf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_18.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_19.png b/core/res/res/drawable-mdpi/pointer_wait_19.png
new file mode 100644
index 0000000..9d51f79
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_19.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_2.png b/core/res/res/drawable-mdpi/pointer_wait_2.png
new file mode 100644
index 0000000..d73a154
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_20.png b/core/res/res/drawable-mdpi/pointer_wait_20.png
new file mode 100644
index 0000000..81d1d51
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_20.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_21.png b/core/res/res/drawable-mdpi/pointer_wait_21.png
new file mode 100644
index 0000000..331820b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_21.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_22.png b/core/res/res/drawable-mdpi/pointer_wait_22.png
new file mode 100644
index 0000000..2678d32
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_22.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_23.png b/core/res/res/drawable-mdpi/pointer_wait_23.png
new file mode 100644
index 0000000..d54d9eb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_23.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_24.png b/core/res/res/drawable-mdpi/pointer_wait_24.png
new file mode 100644
index 0000000..442ace7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_24.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_25.png b/core/res/res/drawable-mdpi/pointer_wait_25.png
new file mode 100644
index 0000000..27ce60d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_25.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_26.png b/core/res/res/drawable-mdpi/pointer_wait_26.png
new file mode 100644
index 0000000..8143634
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_26.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_27.png b/core/res/res/drawable-mdpi/pointer_wait_27.png
new file mode 100644
index 0000000..496ab9a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_27.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_28.png b/core/res/res/drawable-mdpi/pointer_wait_28.png
new file mode 100644
index 0000000..a2aab2b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_28.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_29.png b/core/res/res/drawable-mdpi/pointer_wait_29.png
new file mode 100644
index 0000000..646d153
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_29.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_3.png b/core/res/res/drawable-mdpi/pointer_wait_3.png
new file mode 100644
index 0000000..9f45afe
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_30.png b/core/res/res/drawable-mdpi/pointer_wait_30.png
new file mode 100644
index 0000000..27b3fc4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_30.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_31.png b/core/res/res/drawable-mdpi/pointer_wait_31.png
new file mode 100644
index 0000000..6dbe184
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_31.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_32.png b/core/res/res/drawable-mdpi/pointer_wait_32.png
new file mode 100644
index 0000000..9f072ef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_32.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_33.png b/core/res/res/drawable-mdpi/pointer_wait_33.png
new file mode 100644
index 0000000..881ec5f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_33.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_34.png b/core/res/res/drawable-mdpi/pointer_wait_34.png
new file mode 100644
index 0000000..94961e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_34.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_35.png b/core/res/res/drawable-mdpi/pointer_wait_35.png
new file mode 100644
index 0000000..dfa65d7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_35.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_4.png b/core/res/res/drawable-mdpi/pointer_wait_4.png
new file mode 100644
index 0000000..5d3d652
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_5.png b/core/res/res/drawable-mdpi/pointer_wait_5.png
new file mode 100644
index 0000000..d440a82
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_6.png b/core/res/res/drawable-mdpi/pointer_wait_6.png
new file mode 100644
index 0000000..ae65590
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_6.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_7.png b/core/res/res/drawable-mdpi/pointer_wait_7.png
new file mode 100644
index 0000000..cd84aa5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_7.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_8.png b/core/res/res/drawable-mdpi/pointer_wait_8.png
new file mode 100644
index 0000000..0b81a9a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_8.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/pointer_wait_9.png b/core/res/res/drawable-mdpi/pointer_wait_9.png
new file mode 100644
index 0000000..c13a90c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointer_wait_9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_0.png b/core/res/res/drawable-xhdpi/pointer_wait_0.png
new file mode 100644
index 0000000..5396784
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_0.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_1.png b/core/res/res/drawable-xhdpi/pointer_wait_1.png
new file mode 100644
index 0000000..25edbf5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_1.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_10.png b/core/res/res/drawable-xhdpi/pointer_wait_10.png
new file mode 100644
index 0000000..96d93a9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_10.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_11.png b/core/res/res/drawable-xhdpi/pointer_wait_11.png
new file mode 100644
index 0000000..cd78675
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_11.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_12.png b/core/res/res/drawable-xhdpi/pointer_wait_12.png
new file mode 100644
index 0000000..1b2c7b2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_12.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_13.png b/core/res/res/drawable-xhdpi/pointer_wait_13.png
new file mode 100644
index 0000000..3b00f10
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_13.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_14.png b/core/res/res/drawable-xhdpi/pointer_wait_14.png
new file mode 100644
index 0000000..eca5c3f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_14.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_15.png b/core/res/res/drawable-xhdpi/pointer_wait_15.png
new file mode 100644
index 0000000..0fc2085
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_15.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_16.png b/core/res/res/drawable-xhdpi/pointer_wait_16.png
new file mode 100644
index 0000000..db13cf6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_16.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_17.png b/core/res/res/drawable-xhdpi/pointer_wait_17.png
new file mode 100644
index 0000000..9b6fac5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_17.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_18.png b/core/res/res/drawable-xhdpi/pointer_wait_18.png
new file mode 100644
index 0000000..c56ff6c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_18.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_19.png b/core/res/res/drawable-xhdpi/pointer_wait_19.png
new file mode 100644
index 0000000..22b7c90
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_19.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_2.png b/core/res/res/drawable-xhdpi/pointer_wait_2.png
new file mode 100644
index 0000000..4bdbe3f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_2.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_20.png b/core/res/res/drawable-xhdpi/pointer_wait_20.png
new file mode 100644
index 0000000..6d042fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_20.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_21.png b/core/res/res/drawable-xhdpi/pointer_wait_21.png
new file mode 100644
index 0000000..e3ab63f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_21.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_22.png b/core/res/res/drawable-xhdpi/pointer_wait_22.png
new file mode 100644
index 0000000..b25f6b7d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_22.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_23.png b/core/res/res/drawable-xhdpi/pointer_wait_23.png
new file mode 100644
index 0000000..49faba9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_23.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_24.png b/core/res/res/drawable-xhdpi/pointer_wait_24.png
new file mode 100644
index 0000000..e91c340
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_24.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_25.png b/core/res/res/drawable-xhdpi/pointer_wait_25.png
new file mode 100644
index 0000000..f4785c6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_25.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_26.png b/core/res/res/drawable-xhdpi/pointer_wait_26.png
new file mode 100644
index 0000000..ea902f8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_26.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_27.png b/core/res/res/drawable-xhdpi/pointer_wait_27.png
new file mode 100644
index 0000000..7d628c3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_27.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_28.png b/core/res/res/drawable-xhdpi/pointer_wait_28.png
new file mode 100644
index 0000000..92d6dc1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_28.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_29.png b/core/res/res/drawable-xhdpi/pointer_wait_29.png
new file mode 100644
index 0000000..5a8d189
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_29.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_3.png b/core/res/res/drawable-xhdpi/pointer_wait_3.png
new file mode 100644
index 0000000..de4d79c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_3.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_30.png b/core/res/res/drawable-xhdpi/pointer_wait_30.png
new file mode 100644
index 0000000..ba04b5e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_30.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_31.png b/core/res/res/drawable-xhdpi/pointer_wait_31.png
new file mode 100644
index 0000000..3ef8e98
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_31.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_32.png b/core/res/res/drawable-xhdpi/pointer_wait_32.png
new file mode 100644
index 0000000..3297a7d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_32.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_33.png b/core/res/res/drawable-xhdpi/pointer_wait_33.png
new file mode 100644
index 0000000..b0ac3b9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_33.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_34.png b/core/res/res/drawable-xhdpi/pointer_wait_34.png
new file mode 100644
index 0000000..0eaa386
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_34.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_35.png b/core/res/res/drawable-xhdpi/pointer_wait_35.png
new file mode 100644
index 0000000..73894d8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_35.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_4.png b/core/res/res/drawable-xhdpi/pointer_wait_4.png
new file mode 100644
index 0000000..ea44e85
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_4.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_5.png b/core/res/res/drawable-xhdpi/pointer_wait_5.png
new file mode 100644
index 0000000..46c399d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_5.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_6.png b/core/res/res/drawable-xhdpi/pointer_wait_6.png
new file mode 100644
index 0000000..3b9aff6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_6.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_7.png b/core/res/res/drawable-xhdpi/pointer_wait_7.png
new file mode 100644
index 0000000..a54edc0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_7.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_8.png b/core/res/res/drawable-xhdpi/pointer_wait_8.png
new file mode 100644
index 0000000..2f30732
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_8.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_wait_9.png b/core/res/res/drawable-xhdpi/pointer_wait_9.png
new file mode 100644
index 0000000..f39c7a7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_wait_9.png
Binary files differ
diff --git a/core/res/res/drawable/non_client_decor_title.xml b/core/res/res/drawable/decor_caption_title.xml
similarity index 84%
rename from core/res/res/drawable/non_client_decor_title.xml
rename to core/res/res/drawable/decor_caption_title.xml
index e50daea..591605d3 100644
--- a/core/res/res/drawable/non_client_decor_title.xml
+++ b/core/res/res/drawable/decor_caption_title.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="true"
- android:drawable="@drawable/non_client_decor_title_focused" />
- <item android:drawable="@drawable/non_client_decor_title_unfocused" />
+ android:drawable="@drawable/decor_caption_title_focused" />
+ <item android:drawable="@drawable/decor_caption_title_unfocused" />
</selector>
diff --git a/core/res/res/drawable/non_client_decor_title_focused.xml b/core/res/res/drawable/decor_caption_title_focused.xml
similarity index 100%
rename from core/res/res/drawable/non_client_decor_title_focused.xml
rename to core/res/res/drawable/decor_caption_title_focused.xml
diff --git a/core/res/res/drawable/non_client_decor_title_unfocused.xml b/core/res/res/drawable/decor_caption_title_unfocused.xml
similarity index 100%
rename from core/res/res/drawable/non_client_decor_title_unfocused.xml
rename to core/res/res/drawable/decor_caption_title_unfocused.xml
diff --git a/core/res/res/drawable/pointer_wait.xml b/core/res/res/drawable/pointer_wait.xml
new file mode 100644
index 0000000..8955ce8
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+ <item android:drawable="@drawable/pointer_wait_1" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_2" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_3" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_4" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_5" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_6" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_7" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_8" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_9" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_10" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_11" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_12" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_13" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_14" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_15" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_16" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_17" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_18" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_19" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_20" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_21" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_22" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_23" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_24" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_25" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_26" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_27" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_28" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_29" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_30" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_31" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_32" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_33" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_34" android:duration="25"/>
+ <item android:drawable="@drawable/pointer_wait_35" android:duration="25"/>
+</animation-list>
diff --git a/core/res/res/drawable/pointer_wait_icon.xml b/core/res/res/drawable/pointer_wait_icon.xml
new file mode 100644
index 0000000..d9b03b0
--- /dev/null
+++ b/core/res/res/drawable/pointer_wait_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_wait"
+ android:hotSpotX="7dp"
+ android:hotSpotY="7dp" />
diff --git a/core/res/res/layout/alert_dialog_button_bar_material.xml b/core/res/res/layout/alert_dialog_button_bar_material.xml
index 6e102f3..f7974a5 100644
--- a/core/res/res/layout/alert_dialog_button_bar_material.xml
+++ b/core/res/res/layout/alert_dialog_button_bar_material.xml
@@ -27,7 +27,6 @@
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:gravity="bottom"
- android:allowStacking="@bool/allow_stacked_button_bar"
style="?attr/buttonBarStyle">
<Button
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index a4388f6..821b588 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -19,19 +19,27 @@
android:id="@+id/date_picker_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="18dp"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
- android:orientation="vertical">
+ android:paddingTop="16dp"
+ android:paddingBottom="18dp"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
- <!-- Top padding should stay on this view so that
- the touch target is a bit larger. -->
<TextView
android:id="@+id/date_picker_header_year"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="16dp"
- android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
+ android:focusable="true"
+ android:layout_marginStart="-8dp"
+ android:layout_marginEnd="-8dp"
+ android:layout_marginTop="-8dp"
+ android:layout_marginBottom="-8dp"
+ android:padding="8dp"
+ android:background="?attr/selectableItemBackground"
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
+ android:nextFocusForward="@+id/prev" />
<TextView
android:id="@+id/date_picker_header_date"
diff --git a/core/res/res/layout/date_picker_month_item_material.xml b/core/res/res/layout/date_picker_month_item_material.xml
index cb79cee..52f7b8e 100644
--- a/core/res/res/layout/date_picker_month_item_material.xml
+++ b/core/res/res/layout/date_picker_month_item_material.xml
@@ -21,4 +21,5 @@
android:layout_height="wrap_content"
android:paddingStart="@dimen/day_picker_padding_horizontal"
android:paddingEnd="@dimen/day_picker_padding_horizontal"
- android:paddingTop="@dimen/day_picker_padding_top" />
+ android:paddingTop="@dimen/day_picker_padding_top"
+ android:focusable="true"/>
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
index b582d74..d77e8dc 100644
--- a/core/res/res/layout/day_picker_content_material.xml
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -33,7 +33,9 @@
android:src="@drawable/ic_chevron_start"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_prev_month_button"
- android:visibility="invisible" />
+ android:visibility="invisible"
+ android:nextFocusForward="@+id/next"
+ android:nextFocusDown="@+id/month_view" />
<ImageButton
android:id="@+id/next"
@@ -44,6 +46,8 @@
android:src="@drawable/ic_chevron_end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_next_month_button"
- android:visibility="invisible" />
+ android:visibility="invisible"
+ android:nextFocusForward="@+id/month_view"
+ android:nextFocusDown="@+id/month_view"/>
</FrameLayout>
diff --git a/core/res/res/layout/non_client_decor_dark.xml b/core/res/res/layout/decor_caption_dark.xml
similarity index 89%
rename from core/res/res/layout/non_client_decor_dark.xml
rename to core/res/res/layout/decor_caption_dark.xml
index 40b8960..273264d 100644
--- a/core/res/res/layout/non_client_decor_dark.xml
+++ b/core/res/res/layout/decor_caption_dark.xml
@@ -17,16 +17,17 @@
*/
-->
-<com.android.internal.widget.NonClientDecorView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="beforeDescendants" >
<LinearLayout
+ android:id="@+id/caption"
android:layout_width="match_parent"
android:layout_gravity="end"
android:layout_height="wrap_content"
- android:background="@drawable/non_client_decor_title"
+ android:background="@drawable/decor_caption_title"
android:focusable="false"
android:descendantFocusability="blocksDescendants" >
<LinearLayout
@@ -53,4 +54,4 @@
android:contentDescription="@string/close_button_text"
android:background="@drawable/decor_close_button_dark" />
</LinearLayout>
-</com.android.internal.widget.NonClientDecorView>
+</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/non_client_decor_light.xml b/core/res/res/layout/decor_caption_light.xml
similarity index 89%
rename from core/res/res/layout/non_client_decor_light.xml
rename to core/res/res/layout/decor_caption_light.xml
index c75d526..fd9198e 100644
--- a/core/res/res/layout/non_client_decor_light.xml
+++ b/core/res/res/layout/decor_caption_light.xml
@@ -17,16 +17,17 @@
*/
-->
-<com.android.internal.widget.NonClientDecorView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="beforeDescendants" >
<LinearLayout
+ android:id="@+id/caption"
android:layout_width="match_parent"
android:layout_gravity="end"
android:layout_height="wrap_content"
- android:background="@drawable/non_client_decor_title"
+ android:background="@drawable/decor_caption_title"
android:focusable="false"
android:descendantFocusability="blocksDescendants" >
<LinearLayout
@@ -53,4 +54,4 @@
android:contentDescription="@string/close_button_text"
android:background="@drawable/decor_close_button_light" />
</LinearLayout>
-</com.android.internal.widget.NonClientDecorView>
+</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index da8b2e7..f4bc918 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -18,15 +18,11 @@
<Button xmlns:android="http://schemas.android.com/apk/res/android"
style="@android:style/Widget.Material.Light.Button.Borderless.Small"
android:id="@+id/action0"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_weight="1"
- android:layout_margin="0dp"
- android:gravity="start|center_vertical"
- android:drawablePadding="8dp"
- android:paddingStart="8dp"
+ android:layout_gravity="center"
+ android:layout_marginStart="8dp"
android:textColor="@color/secondary_text_material_light"
- android:textSize="13sp"
android:singleLine="true"
android:ellipsize="end"
android:background="@drawable/notification_material_action_background"
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 2a36949..edaf020 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -14,14 +14,20 @@
limitations under the License.
-->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/actions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone"
- android:layout_marginBottom="8dp"
- >
- <!-- actions will be added here -->
-</LinearLayout>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ android:paddingEnd="8dp"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:background="#ffeeeeee"
+ >
+ <!-- actions will be added here -->
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index f073c33..fc53a1a 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -24,18 +24,23 @@
android:background="@android:color/transparent"
android:layout_removeBorders="true">
- <ListView android:id="@android:id/list"
- style="?attr/preferenceFragmentListStyle"
+ <FrameLayout
+ android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="0px"
- android:layout_weight="1"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
- android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
+ android:layout_weight="1">
+ <ListView android:id="@android:id/list"
+ style="?attr/preferenceFragmentListStyle"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="0dip"
+ android:paddingBottom="@dimen/preference_fragment_padding_bottom"
+ android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+ </FrameLayout>
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml
index 62bdffd..e411c0e 100644
--- a/core/res/res/layout/preference_list_fragment_material.xml
+++ b/core/res/res/layout/preference_list_fragment_material.xml
@@ -24,18 +24,23 @@
android:background="@android:color/transparent"
android:layout_removeBorders="true">
- <ListView android:id="@android:id/list"
- style="?attr/preferenceFragmentListStyle"
+ <FrameLayout
+ android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="0px"
- android:layout_weight="1"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
- android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
+ android:layout_weight="1">
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="0dip"
+ android:paddingBottom="@dimen/preference_fragment_padding_bottom"
+ style="?attr/preferenceFragmentListStyle"
+ android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+ </FrameLayout>
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index 25e8aa6..80c572b 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -17,7 +17,7 @@
<!-- Layout used by SwitchPreference for the switch widget style. This is inflated
inside android.R.layout.preference. -->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+android:id/switchWidget"
+ android:id="@+android:id/switch_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
diff --git a/core/res/res/layout/text_edit_suggestion_container.xml b/core/res/res/layout/text_edit_suggestion_container.xml
new file mode 100644
index 0000000..04eca8f
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_container.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:divider="@null">
+ <ListView
+ android:id="@+id/suggestionContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerHorizontal">
+ <!-- Suggestions will be added here. -->
+ </ListView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="middle">
+ <TextView
+ style="@android:style/Widget.Holo.SuggestionButton"
+ android:id="@+id/addToDictionaryButton"
+ android:text="@string/addToDictionary" />
+ <TextView
+ style="@android:style/Widget.Holo.SuggestionButton"
+ android:id="@+id/deleteButton"
+ android:text="@string/deleteText" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
new file mode 100644
index 0000000..d0e2467
--- /dev/null
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="middle" >
+ <ListView
+ android:id="@+id/suggestionContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/white"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:divider="@null" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ style="@android:style/Widget.Material.SuggestionButton"
+ android:id="@+id/addToDictionaryButton"
+ android:text="@string/addToDictionary" />
+ <TextView
+ style="@android:style/Widget.Material.SuggestionButton"
+ android:id="@+id/deleteButton"
+ android:text="@string/deleteText" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/text_edit_suggestion_item.xml b/core/res/res/layout/text_edit_suggestion_item.xml
index a965ddd..9dcbf2e 100644
--- a/core/res/res/layout/text_edit_suggestion_item.xml
+++ b/core/res/res/layout/text_edit_suggestion_item.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,16 +15,5 @@
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:layout_gravity="start|center_vertical"
- android:singleLine="true"
- android:drawablePadding="8dip"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@android:color/dim_foreground_light" />
+ style="@android:style/Widget.Holo.SuggestionItem" />
diff --git a/core/res/res/values-h320dp/bools.xml b/core/res/res/layout/text_edit_suggestion_item_material.xml
similarity index 76%
rename from core/res/res/values-h320dp/bools.xml
rename to core/res/res/layout/text_edit_suggestion_item_material.xml
index 3bbfe96..0443a97 100644
--- a/core/res/res/values-h320dp/bools.xml
+++ b/core/res/res/layout/text_edit_suggestion_item_material.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,6 @@
limitations under the License.
-->
-<resources>
- <bool name="allow_stacked_button_bar">true</bool>
-</resources>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.SuggestionItem" />
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f8b88a7..3ac1491 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skripte kan geïnstalleer word om program-inhoud meer toeganklik te maak."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Neem teks wat jy tik waar"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sluit persoonlike data soos kredietkaartnommers en wagwoorde in."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Beheer vertoonskermvergroting"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Beheer die vertoonskerm se zoemvlak en posisionering."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiveer of verander statusbalk"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"wees die statusbalk"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gekies</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gekies</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d9246bc..f8c4158 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"የመተግበሪያ ይዘት ይበልጥ የሚገኙ ለማድረግ ስክሪፕቶች ሊጫኑ ይችላሉ።"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"የሚተይቡት ጽሑፍ ይመልከቱ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"እንደ የክሬዲት ካርድ ቁጥሮች እና የይለፍ ቃላት ያሉ የግል ውሂብ ያካትታል።"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"የመቆጣጠሪያ ማሳያ እንዲጎላ አደራረግ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"የማሳያውን የማጉያ ደረጃ እና አቀማመጥ ይቆጣጠሩ።"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"የሁኔቴ አሞሌ አቦዝን ወይም ቀይር"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"የሁኔታ አሞሌ መሆን"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጧል</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c62fb2c..938b31e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"قد يتم تثبيت النصوص البرمجية لتسهيل الدخول إلى المحتوى."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ملاحظة النص الذي تكتبه"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"التحكم في تكبير الشاشة"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"يمكنك التحكم في مستوى التكبير/التصغير للشاشة وتحديد الموضع."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"تعطيل شريط الحالة أو تعديله"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"للسماح للتطبيق بتعطيل شريط الحالة أو إضافة رموز نظام وإزالتها."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"العمل كشريط للحالة"</string>
@@ -1571,4 +1573,5 @@
<item quantity="other">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g> من العناصر</item>
<item quantity="one">تم تحديد <xliff:g id="COUNT_0">%1$d</xliff:g> عنصر</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"متنوعة"</string>
</resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index db91232..b22b553 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skriptlər tətbiq məzmununun daha əlçatımlı olması üçün quraşdırıla bilər."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Yazdığınız mətni izləyin"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran böyütməsinə nəzarət edin"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekran yaxınlaşdırma səviyyəsi və yerləşdirməsinə nəzarət edin."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"status panelini deaktivləşdir və ya dəyişdir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"status paneli edin"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seçilib</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seçilib</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index fcab6d0..7093cec 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Скриптовете може да бъдат инсталирани, за да направят съдържанието от приложенията по-достъпно."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Наблюдение на въвеждания от вас текст"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включва лични данни, като например номера на кредитни карти и пароли."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управление на увеличението на дисплея"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управление на нивото на мащаба и позиционирането на дисплея."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"деактивиране или промяна на лентата на състоянието"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"изпълняване на ролята на лента на състоянието"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">Избрахте <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Избрахте <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index d66c249..754bf88 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"অ্যাপ্লিকেশানের সামগ্রীকে আরো অ্যাক্সেসযোগ্য করতে স্ক্রিপ্টগুলি ইনস্টল করা হতে পারে৷"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"আপনার লেখা পাঠ্যকে নিরীক্ষণ করে"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্রেডিট কার্ডের নম্বর ও পাসওয়ার্ডগুলির মতো ব্যক্তিগত তথ্য অন্তর্ভুক্ত করে৷"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"প্রদর্শনের বৃহত্তরীকরণ ব্যবস্থা নিয়ন্ত্রণ করুন"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"প্রদর্শনের জুমের স্তর এবং অবস্থান নির্ধারন নিয়ন্ত্রণ করুন৷"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দন্ড নিষ্ক্রিয় অথবা সংশোধন করে"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"অ্যাপ্লিকেশানকে স্থিতি দন্ড অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"স্থিতি দন্ডে থাকুন"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"বিবিধ"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fae3b41..243b96d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"És possible que s\'instal·lin scripts perquè el contingut de les aplicacions sigui més accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar el text que escrius"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclou dades personals com ara números de targetes de crèdit i contrasenyes."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla l\'ampliació de la pantalla"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el nivell i el posicionament del zoom de la pantalla."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparèixer a la barra d\'estat"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">Seleccionats: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Seleccionats: <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index b68d1ef..8869b25 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Za účelem usnadnění přístupu k obsahu aplikací mohou být nainstalovány skripty."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Sledovat zadávaný text"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledování zahrnuje osobní údaje, jako jsou například čísla kreditních karet a hesla."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nastavení zvětšení obsahu obrazovky"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Určuje umístění a úroveň přiblížení displeje."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázání či změny stavového řádku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vydávání se za stavový řádek"</string>
@@ -1535,4 +1537,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> položek</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> položka</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index f83828f..9e7a1e6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Der installeres muligvis scripts for at gøre appindhold mere tilgængeligt."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"observere tekst, du skriver"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data såsom kreditkortnumre og adgangskoder."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér skærmforstørrelsen"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér skærmens zoomniveau og position."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vær statusbjælken"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>valgt</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valgt</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Diverse"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ea6239c..e02f926 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skripts können installiert werden, um den Zugriff auf App-Inhalte zu erleichtern."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Text bei der Eingabe beobachten"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe des Displays und die Zoom-Position auf dem Display fest."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Statusleiste darstellen"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ausgewählt</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ausgewählt</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 83ad7de..8450552 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Ενδέχεται να εγκατασταθούν σενάρια για τη βελτίωση της πρόσβασης στο περιεχόμενο της εφαρμογής."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Παρακολούθηση του κειμένου που πληκτρολογείτε"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Περιλαμβάνει προσωπικά δεδομένα, όπως είναι οι αριθμοί πιστωτικών καρτών και οι κωδικοί πρόσβασης."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ελέγξτε τη μεγέθυνση της οθόνης"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ελέγξτε το επίπεδο ζουμ και τη θέση της οθόνης."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ορισμός ως γραμμής κατάστασης"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">Επιλέχτηκαν <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Επιλέχτηκε <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7ff1ef3..f3b1e1a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scripts may be installed to make app content more accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observe text that you type"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Miscellaneous"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7ff1ef3..1db1975 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scripts may be installed to make app content more accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observe text that you type"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7ff1ef3..f3b1e1a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scripts may be installed to make app content more accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observe text that you type"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Miscellaneous"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index bc05551..e3cccde 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Es posible que se instalen secuencias de comandos para que el contenido de las aplicaciones sea más accesible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar el texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar la ampliación de pantalla"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementos seleccionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento seleccionado</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index aadf387..c00b04b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Es posible que se instalen secuencias de comandos para que el contenido de las aplicaciones sea más accesible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar el texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales como números de tarjetas de crédito y contraseñas."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla la ampliación de la pantalla"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inhabilitar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seleccionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seleccionado</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 4aed72d..dab6293 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Rakenduse sisu kättesaadavamaks muutmiseks võidakse installida skripte."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Sisestatud teksti jälgimine"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisaldab isiklikke andmeid, nt krediitkaardi numbreid ja paroole."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekraani suurenduse juhtimine"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Saate juhtida ekraani suumitaset ja asendit."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"keela või muuda olekuriba"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"olekuribana kuvamine"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> on valitud</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> on valitud</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Mitmesugust"</string>
</resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 9f10506..11eb19c 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scriptak instala daitezke aplikazioaren edukia erabilerrazagoa egiteko."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Behatu idazten duzun testua"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolatu pantailaren zoom-maila"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolatu pantailaren zoom-maila eta kokapena."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Desgaitu edo aldatu egoera-barra"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Bihurtu egoera-barra"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> hautatuta</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> hautatuta</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Askotarikoak"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2a41913..add3850 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ممکن است جهت افزایش دسترسپذیری به محتوای برنامه، اسکریپتهایی نصب شود."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"نوشتاری را که تایپ میکنید مشاهده کند"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اطلاعات شخصی مانند شماره کارت اعتباری و گذرواژهها را لحاظ میکند."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"کنترل درشتنمایی نمایشگر"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"سطح و موقعیت بزرگنمایی نمایشگر را کنترل کنید."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"غیرفعال کردن یا تغییر نوار وضعیت"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"به برنامه اجازه میدهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"نوار وضعیت باشد"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b83307e..12e75eb 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Sovellus voi asentaa ohjelmia tehdäkseen sisällöstään esteettömämmän."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Tarkkailla kirjoittamaasi tekstiä"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisältää henkilökohtaisia tietoja, kuten luottokortin numeroita ja salasanoja."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Näytön suurentamisen hallinnointi"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Hallinnoi näytön zoomaustasoa ja asettelua."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"sijaita tilapalkissa"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valittu</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> valittu</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 6f54ece..6206138 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Vous pouvez installer des scripts pour rendre le contenu des applications plus accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observer le texte que vous saisissez"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"désactiver ou modifier la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"servir de barre d\'état"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b58c0bd..7fb641e 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Vous pouvez installer des scripts pour rendre le contenu des applications plus accessible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observer le texte que vous saisissez"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Désactivation ou modification de la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"remplacer la barre d\'état"</string>
@@ -420,7 +422,7 @@
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autoriser l\'application à utiliser le matériel d\'empreintes digitales pour l\'authentification"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le lecteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Vous avez déplacé votre doigt trop rapidement. Veuillez réessayer."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index ab24047..982e18c 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"É posible que se instalen scripts para que o contido da aplicación resulte máis accesible."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar o texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclúe datos persoais como números e contrasinais de tarxetas de crédito."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliación da pantalla"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nivel do zoom e o posicionamento da pantalla"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar a barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"actuar como a barra de estado"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">Seleccionáronse <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Seleccionouse <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 93faa70..2d3af4d 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"એપ્લિકેશન સામગ્રીને વધુ ઍક્સેસિબલ બનાવવા માટે સ્ક્રિપ્ટ્સ ઇન્સ્ટોલ કરી શકાય છે."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"તમે લખો તે ટેક્સ્ટનું અવલોકન કરો"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"પ્રદર્શન વિસ્તૃતિકરણ નિયંત્રિત કરો"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"પ્રદર્શનનું ઝૂમ સ્તર અને સ્થિતિનિર્ધારણ નિયંત્રિત કરો."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"સ્થિતિ બાર અક્ષમ કરો અથવા સંશોધિત કરો"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"એપ્લિકેશનને સ્થિતિ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"સ્થિતિ બાર થાઓ"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"વિવિધ"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6dba1fb..75e782c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ऐप्स सामग्री को अधिक पहुंच-योग्य बनाने के लिए स्क्रिप्ट इंस्टॉल किए जा सकते हैं."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"आपके द्वारा लिखे हुए लेख को ध्यान से देखें"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर और पासवर्ड जैसा व्यक्तिगत डेटा शामिल होता है."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियंत्रित करें"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शन का ज़ूम स्तर और स्थिति निर्धारण नियंत्रित करें."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति बार अक्षम या बदलें"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ऐप्स को स्थिति बार अक्षम करने या सिस्टम आइकन को जोड़ने या निकालने देता है."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्थिति बार होने दें"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"विविध"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 643cf0d..a0d8cc8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -253,6 +253,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Kako bi sadržaj aplikacije bio pristupačniji, mogu se instalirati skripte."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Pratiti tekst koji pišete"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Uključuje osobne podatke kao što su brojevi kreditnih kartica i zaporke."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrola uvećanja zaslona"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolira razinu zumiranja i položaj zaslona."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmjena trake statusa"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"biti traka statusa"</string>
@@ -1517,4 +1519,6 @@
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> odabrane</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> odabranih</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f4b6346..83ceaa0 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Szkripteket lehet telepíteni, hogy könnyebb legyen hozzáférni az alkalmazások tartalmához."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"A gépelt szöveg figyelése"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Beleértve a személyes adatokat, például a hitelkártyaszámokat és jelszavakat."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"A kijelző nagyításának vezérlése"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"A kijelző nagyítási/kicsinyítési szintjének és pozíciójának vezérlése"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"állapotsor kikapcsolása vagy módosítása"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"az állapotsor szerepének átvétele"</string>
@@ -699,7 +701,7 @@
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Fiók feloldása"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Túl sok mintarajzolási próbálkozás"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"A feloldáshoz jelentkezzen be Google-fiókjával."</string>
- <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Felhasználónév (e-mail cím)"</string>
+ <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Felhasználónév (e-mail-cím)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Jelszó"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Bejelentkezés"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Érvénytelen felhasználónév vagy jelszó."</string>
@@ -1277,7 +1279,7 @@
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"A PIN kódok nem egyeznek."</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Túl sok mintarajzolási próbálkozás"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"A feloldáshoz jelentkezzen be Google-fiókjával."</string>
- <string name="kg_login_username_hint" msgid="5718534272070920364">"Felhasználónév (e-mail cím)"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Felhasználónév (e-mail-cím)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"Jelszó"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"Bejelentkezés"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"Érvénytelen felhasználónév vagy jelszó."</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> kiválasztva</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kiválasztva</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Vegyes"</string>
</resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index e829a76..ad23ce4 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Հնարավոր է սկրիպտներ տեղադրվեն` ծրագրի բովանդակությունն ավելի մատչելի դարձնելու համար:"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Զննել ձեր մուտքագրած տեքստը"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ներառում է անձնական տվյալներ, ինչպիսիք են վարկային քարտերի համարները և գաղտնաբառերը:"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ցուցասարքի խոշորացման կառավարում"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ցուցասարքի մասշտաբավորման և դիրքավորման կառավարում:"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"անջատել կամ փոփոխել կարգավիճակի գոտին"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Թույլ է տալիս հավելվածին անջատել կարգավիճակի գոտին կամ ավելացնել ու հեռացնել համակարգի պատկերակները:"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"լինել կարգավիճակի գոտի"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f626389..a1a6131 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skrip mungkin dipasang agar konten aplikasi lebih dapat diakses."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Mengamati teks yang Anda ketik"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Mengontrol perbesaran layar"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Mengontrol tingkat zoom dan pemosisian layar."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"nonaktifkan atau ubah bilah status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"jadikan bilah status"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 3ebbad7..f0f4e70 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Hægt er að setja upp skriftur til að bæta aðgengi að efni forrits."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Fylgjast með texta sem þú slærð inn"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Felur í sér persónuleg gögn á borð við kreditkortanúmer og aðgangsorð."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Stilla skjástærð"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Stjórnaðu aðdrætti og afstöðu skjásins."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"slökkva á eða breyta stöðustiku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vera stöðustikan"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> valið</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valin</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 521eae2..4f39543 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Potrebbero essere installati script per rendere più accessibili i contenuti delle app."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Osservare il testo digitato"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlla l\'ingrandimento del display"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlla il livello di zoom e la posizione del display."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disattivare o modificare la barra di stato"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ruolo di barra di stato"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementi selezionati</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento selezionato</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4eb153a..3efa5a7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ייתכן שסקריפטים יותקנו על מנת להקל את הגישה אל תוכן של אפליקציות."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"הצגת טקסט בזמן הקלדה"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"כולל נתונים אישיים כמו מספרי כרטיס אשראי וסיסמאות."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"שליטה בהגדלת התצוגה"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"קבע את המרחק מהתצוגה ואת מיקום התצוגה."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"להיות שורת הסטטוס"</string>
@@ -1535,4 +1537,6 @@
<item quantity="other">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">בחרת <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f900301..28b09fa 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"スクリプトをインストールしてアプリコンテンツにアクセスしやすくできます。"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"入力テキストの監視"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"クレジットカードの番号やパスワードなどの個人データが含まれます。"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"画面の拡大の制御"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"画面のズームレベルと位置を制御します。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ステータスバーの無効化や変更"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ステータスバーへの表示"</string>
@@ -420,7 +422,7 @@
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"指紋ハードウェアを認証に使用することをアプリに許可します"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋認証センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"指の動きが速すぎました。もう一度お試しください。"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"指の動きが遅すぎました。もう一度お試しください。"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>件選択済み</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>件選択済み</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 94988ab..1a126e8 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"შესაძლებელია სკრიპტების ინსტალაცია აპის კონტენტის წვდომადობის უზრუნველსაყოფად."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"თქვენ მიერ აკრეფილ ტექსტზე დაკვირვება"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"შეიცავს ისეთ პირად მონაცემებს, როგორიცაა საკრედიტო ბარათის ნომრები და პაროლები."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ერანის გადიდების მართვა"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ეკრანის მასშტაბირების დონისა და პოზიციის მართვა."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"სტატუსის ზოლის გათიშვა ან ცვლილება"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"აპს შეეძლება სტატუსების ზოლის გათიშვა და სისტემის ხატულების დამატება/წაშლა."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"სტატუსის ზოლის ჩანაცვლება"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> შერჩეული</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> შერჩეული</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 8e45087..854190a 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Қолданба мазұнына кіруді жеңілдету үшін скрипт орнатылуы мүмкін."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Терілген мәтінді тексеру"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит карта нөмірі және кілтсөздер сияқты жеке деректерді қоса."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплей ұлғайтуды басқару"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дисплейдің масштабтау деңгейін және орналастыруды басқару."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"күйін көрсету тақтасын өшіру немесе өзгерту"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Қолданбаға күй жолағын өшіруге немесе жүйелік белгішелерді қосуға және жоюға рұқсат береді."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"күй жолағы болу"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> таңдалды</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> таңдалды</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 69fe257..7abe22b 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ស្គ្រីបអាចត្រូវបានដំឡើង ដើម្បីធ្វើឲ្យមាតិកាកម្មវិធីអាចចូលដំណើរការបានកាន់តែច្រើន។"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"មើលអត្ថបទដែលវាយ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"រួមបញ្ចូលទិន្នន័យផ្ទាល់ខ្លួន ដូចជាលេខកាតឥណទាន និងពាក្យសម្ងាត់។"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"គ្រប់គ្រងការពង្រីកអេក្រង់"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"គ្រប់គ្រងការកំណត់ទីតាំង និងកម្រិតពង្រីករបស់អេក្រង់"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"បិទ ឬកែរបារស្ថានភាព"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ឲ្យកម្មវិធីបិទរបារស្ថានភាព ឬបន្ថែម និងលុបរូបតំណាងប្រព័ន្ធ។"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ធ្វើជារបារស្ថានភាព"</string>
@@ -1501,4 +1503,5 @@
<item quantity="other">បានជ្រើស <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">បានជ្រើស <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"ផ្សេងៗ"</string>
</resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index f3ff668..7e8820e 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ಅಪ್ಲಿಕೇಶನ್ ವಿಷಯ ಇನ್ನಷ್ಟು ಲಭ್ಯವಾಗುವಂತೆ ಮಾಡಲು ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಬಹುದಾಗಿದೆ."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ನೀವು ಟೈಪ್ ಮಾಡುವ ಪಠ್ಯವನ್ನು ಗಮನಿಸುತ್ತದೆ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಪಾಸ್ವರ್ಡ್ಗಳಂತಹ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ಪ್ರದರ್ಶನದ ವರ್ಧಕವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ಪ್ರದರ್ಶನದ ಝೂಮ್ ಮಟ್ಟ ಮತ್ತು ಸ್ಥಾನ ನಿರ್ಧಾರವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"ಇತರೆ"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index fbc6ac2..9796e8e 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"스크립트를 설치하여 앱 콘텐츠에 더 간편하게 액세스할 수 있습니다."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"입력하는 텍스트 살펴보기"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"신용카드 번호와 비밀번호 등의 개인 데이터를 포함합니다."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"디스플레이 배율 제어"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"디스플레이의 확대/축소 수준 및 위치를 제어합니다."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"상태 표시줄에 위치"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>개 선택됨</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>개 선택됨</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 5263dd8..ad60fa5 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Колдонмонун мазмунун жеткиликтүүрөөк кылыш үчүн скрипттер орнотулушу мүмкүн."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Терип жаткан текстти текшерүү"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредиттик карта номурлары жана сырсөздөр сыяктуу өздүк берилиштерди камтыйт."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплейди чоңойтууну башкаруу"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дисплейдин чен өлчөмүн өзгөртүү деңгээли жана жайгаштыруу."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"абал тилкесин өчүрүү же өзгөртүү"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"абал тилкесинин милдетин аткаруу"</string>
@@ -1500,4 +1502,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> тандалды</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> тандалды</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 805982a..0a2b75c 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ສະຄຣິບອາດຖືກຕິດຕັ້ງ ເພື່ອເຮັດໃຫ້ເນື້ອຫາແອັບຯເຂົ້າເຖິງໄດ້ຫຼາຍຂຶ້ນ."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ຕິດຕາມເບິ່ງຂໍ້ຄວາມທີ່ທ່ານພິມ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ຮວມທັງຂໍ້ມູນສ່ວນໂຕເຊັ່ນ: ເລກບັດເຄຣດິດ ແລະລະຫັດຜ່ານ."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ຄວບຄຸມການຂະຫຍາຍຈໍສະແດງຜົນ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ຄວບຄຸມລະດັບການຊູມ ແລະການວາງຕຳແໜ່ງຂອງຈໍສະແດງຜົນ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ປິດການນນຳໃຊ້ ຫຼື ແກ້ໄຂແຖບສະຖານະ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງແຖບສະຖານະ ຫຼືເພີ່ມ ແລະລຶບໄອຄອນລະບົບອອກໄດ້."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ເປັນແຖບສະຖານະ"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ຖືກເລືອກແລ້ວ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ຖືກເລືອກແລ້ວ</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"ອື່ນໆ"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 3d3e333..3224dbc 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Gali būti įdiegti scenarijai, kad būtų lengviau pasiekti programų turinį."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Stebėti jūsų įvedamą tekstą"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Įtraukiami asmeniniai duomenys, pavyzdžiui, kredito kortelių numeriai ir slaptažodžiai."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekrano didinimo valdymas"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Valdykite ekrano mastelio keitimo lygį ir pozicijos nustatymą."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"išjungti ar keisti būsenos juostą"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"būti būsenos juosta"</string>
@@ -1535,4 +1537,5 @@
<item quantity="many">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item>
<item quantity="other">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Įvairūs"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f233737..e984bba 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -253,6 +253,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Var tikt instalēti skripti, lai padarītu lietotņu saturu pieejamāku."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Skatīt ierakstīto tekstu."</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ietver personas datus, piemēram, kredītkartes numurus un paroles."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displeja palielinājuma kontrole"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolējiet displeja tālummaiņas līmeni un pozicionēšanu."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"atspējot vai pārveidot statusa joslu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Būt par statusa joslu"</string>
@@ -1517,4 +1519,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīts</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīti</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mcc208-mnc01/config.xml b/core/res/res/values-mcc208-mnc01/config.xml
index c56da24..5930e3a 100644
--- a/core/res/res/values-mcc208-mnc01/config.xml
+++ b/core/res/res/values-mcc208-mnc01/config.xml
@@ -28,9 +28,6 @@
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string-array translatable="false" name="config_tether_apndata">
<item>Orange Internet,orange.fr,,,orange,orange,,,,,208,01,1,DUN</item>
- <item>[ApnSettingV3]Carrefour WAP,ofnew.fr,,,orange,orange,,,,,208,01,1,DUN,,,true,0,,,,,,,gid,33</item>
- <item>[ApnSettingV3]VM WAP,ofnew.fr,,,orange,orange,,,,,208,01,1,DUN,,,true,0,,,,,,,gid,52</item>
- <item>[ApnSettingV3]NRJWEB,ofnew.fr,,,orange,orange,,,,,208,01,1,DUN,,,true,0,,,,,,,gid,4E</item>
</string-array>
</resources>
diff --git a/core/res/res/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml
index a32f266..d3640e5 100644
--- a/core/res/res/values-mcc208-mnc10/config.xml
+++ b/core/res/res/values-mcc208-mnc10/config.xml
@@ -29,11 +29,6 @@
<string-array translatable="false" name="config_tether_apndata">
<item>SFR option modem,websfr,,,,,,,,,208,10,,DUN</item>
<item>[ApnSettingV3]INTERNET NRJ,internetnrj,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,4E</item>
- <item>[ApnSettingV3]Auchan,wap65,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,spn,A MOBILE</item>
- <item>[ApnSettingV3]LeclercMobile,wap66,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,spn,LeclercMobile</item>
- <item>[ApnSettingV3]Coriolis,fnetcoriolis,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,12</item>
- <item>[ApnSettingV3]WEB La Poste Mobile,wapdebitel,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,4C</item>
- <item>[ApnSettingV3]Darty Surf Mails,wap68,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,44</item>
</string-array>
<string-array translatable="false" name="config_operatorConsideredNonRoaming">
diff --git a/core/res/res/values-mcc214-mnc07/config.xml b/core/res/res/values-mcc214-mnc07/config.xml
index 91571a5..4b7cc7c 100644
--- a/core/res/res/values-mcc214-mnc07/config.xml
+++ b/core/res/res/values-mcc214-mnc07/config.xml
@@ -28,7 +28,6 @@
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string-array translatable="false" name="config_tether_apndata">
<item>Conexión Compartida,movistar.es,,,MOVISTAR,MOVISTAR,,,,,214,07,1,DUN</item>
- <item>[ApnSettingV3]Jazztel Internet,jazzinternet,,,,,,,,,214,07,,DUN,,,true,0,,,,,,,spn,JAZZTEL</item>
</string-array>
</resources>
diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml
index 5a74462..cd6e8c6 100644
--- a/core/res/res/values-mcc222-mnc10/config.xml
+++ b/core/res/res/values-mcc222-mnc10/config.xml
@@ -20,16 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. Do not translate. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
- <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- <item>1</item>
- <item>4</item>
- <item>7</item>
- <item>9</item>
- </integer-array>
-
<!-- String containing the apn value for tethering. May be overriden by secure settings
TETHER_DUN_APN. Value is a comma separated series of strings:
"name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
diff --git a/core/res/res/values-mcc234-mnc20/config.xml b/core/res/res/values-mcc234-mnc20/config.xml
index 814960a..27c91d2 100644
--- a/core/res/res/values-mcc234-mnc20/config.xml
+++ b/core/res/res/values-mcc234-mnc20/config.xml
@@ -21,16 +21,6 @@
for different hardware and product builds. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
- <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- <item>1</item>
- <item>4</item>
- <item>7</item>
- <item>9</item>
- </integer-array>
-
<!-- String containing the apn value for tethering. May be overriden by secure settings
TETHER_DUN_APN. Value is a comma separated series of strings:
"name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 62ba912..a48fe3a 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"За содржината на апликацијата да биде подостапна, може да се инсталираат скрипти."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Набљудувај го напишаниот текст"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Опфаќа лични податоци како што се броеви на кредитни картички и лозинки."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролирајте го зголемувањето на екранот"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролирајте го нивото на зумирање и позиционирање на екранот."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"оневозможи или измени статусна лента"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"да стане статусна лента"</string>
@@ -1501,4 +1503,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> е избрана</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> се избрани</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Разно"</string>
</resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 925688d..b1fbd03 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"അപ്ലിക്കേഷൻ ഉള്ളടക്കം കൂടുതൽ ആക്സസ്സുചെയ്യാൻ കഴിയുന്നതാക്കാൻ സ്ക്രിപ്റ്റുകൾ ഇൻസ്റ്റാളുചെയ്യാനിടയുണ്ട്."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"നിങ്ങൾ ടൈപ്പുചെയ്യുന്ന വാചകം നിരീക്ഷിക്കുക"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ക്രെഡിറ്റ് കാർഡ് നമ്പറുകളും പാസ്വേഡുകളും പോലുള്ള വ്യക്തിഗത ഡാറ്റ ഉൾപ്പെടുന്നു."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ഡിസ്പ്ലേ മാഗ്നിഫിക്കേഷൻ നിയന്ത്രിക്കുക"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്ക്കരിക്കുക"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ef01668..8f28cb6 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Апп контентод илүү хялбар хандуулахын тулд скриптыг суулгана."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Бичсэн текстээ ажиглах"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дэлгэцийн өсгөлтийг хянах"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дэлгэцийн томруулах түвшин болон байршлыг хянах."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"статус самбарыг идэвхгүй болгох болон өөрчлөх"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Апп нь статус самбарыг идэвхгүй болгох эсвэл систем дүрсийг нэмэх, хасах боломжтой."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"статусын хэсэг болох"</string>
@@ -1497,4 +1499,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> сонгосон</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> сонгосон</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 4e43265..8c7b2ff 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"अॅप सामग्री अधिक प्रवेशयोग्य बनविण्यासाठी कदाचित स्क्रिप्ट स्थापित केली जाऊ शकतात."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"आपण टाइप करता त्या मजकुराचे निरीक्षण करा"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्द यासारखा वैयक्तिक डेटा समाविष्ट करते."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन विस्तृतीकरण नियंत्रित करा"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनाचा झूम स्तर आणि स्थिती निर्धारण नियंत्रित करा."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टीम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडला</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडले</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"संकीर्ण"</string>
</resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index a1955bf..fb5cd38 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skrip boleh dipasang untuk menjadikan kandungan apl lebih mudah diakses."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Perhatikan teks yang anda taip"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Mengawal pembesaran paparan"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Mengawal tahap zum dan kedudukan paparan."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"lumpuhkan atau ubah suai bar status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"jadi bar status"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Pelbagai"</string>
</resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a913e38..6cdfb23 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"အပလီကေးရှင်းကို ပိုမိုပြည့်စုံစေရန် စကရစ်များကို သွင်းနိုင်ပါတယ်"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ရိုက်သောစာများကို သေချာစွာ စစ်ဆေးပါ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"အရေးကြီးသော ကိုယ်ရေးအချက်အလက်များဖြစ်တဲ့ ခရက်ဒစ်ကဒ်နံပါတ်များနှင့် စကားဝှက်များ ပါဝင်ပါတယ်."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"မျက်နှာပြင် ချဲ့ခြင်းကို ထိန်းချုပ်ပါ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"မျက်နှာပြင် ချဲ့ခြင်းနှင့် နေရာချထားခြင်းကို ထိန်းချုပ်ပါ"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"appအား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"အခြေအနေပြ ဘားဖြစ်ပါစေ"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"အထွေထွေ"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 936ba59..ed90aa5 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -229,7 +229,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"se kontaktene dine"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Posisjon"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"tilgang til enhetens plassering"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"få tilgang til enhetens plassering"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"åpne kalenderen din"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skript kan installeres for å gjøre appinnhold mer tilgjengelig."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"observere teksten du skriver inn"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data, som kredittkortnumre og passord."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér forstørrelse for skjermen"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér zoomenivået og plasseringen for skjermen."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktivere eller endre statusfeltet"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vise appen i statusfeltet"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> er valgt</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> er valgt</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 6fe9c4d..e2f894c 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"अनुप्रयोगको सामग्रीलाई थप पहुँचयोग्य बनाउन लिपिहरू स्थापना गर्न सक्नु हुन्छ।"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"आफुले टाइप गरेको पाठको निरीक्षण गर्नुहोस्"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"व्यक्तिगत डेटा जस्तै क्रेडिट कार्ड नम्बरहरू र पासवर्डहरू समावेश गर्दछ।"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियन्त्रण गर्नुहोस्"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनको जुम स्तर र स्थिति नियन्त्रण गर्नुहोस्।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"वस्तुस्थिति पट्टी हुन दिनुहोस्"</string>
@@ -1505,4 +1507,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयन गरियो</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> चयन गरियो</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e4f388f..11ae17f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Er kunnen scripts worden geïnstalleerd om app-inhoud toegankelijker te maken."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Tekst observeren die u typt"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omvat persoonlijke gegevens zoals creditcardnummers en wachtwoorden."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Schermvergroting bedienen"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Bedien het zoomniveau en de positionering van het scherm."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"statusbalk uitschakelen of wijzigen"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Hiermee kan de app de statusbalk uitschakelen of systeempictogrammen toevoegen en verwijderen."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"de statusbalk zijn"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> geselecteerd</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> geselecteerd</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Diversen"</string>
</resources>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 96343d8..b33c813 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਵੱਧ ਪਹੁੰਚਯੋਗ ਬਣਾਉਣ ਲਈ ਸਕ੍ਰਿਪਟਾਂ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕਦੀਆਂ।"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ਜੋ ਟੈਕਸਟ ਤੁਸੀਂ ਟਾਈਪ ਕਰਦੇ ਹੋ, ਉਸਦਾ ਨਿਰੀਖਣ ਕਰੋ"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ਇਸ ਵਿੱਚ ਨਿੱਜੀ ਡਾਟਾ ਸ਼ਾਮਲ ਹੈ ਜਿਵੇਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ ਅਤੇ ਪਾਸਵਰਡ।"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ਡਿਸਪਲੇ ਵੱਡਦਰਸ਼ੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ਡਿਸਪਲੇ ਦੇ ਜ਼ੂਮ ਪੱਧਰ ਅਤੇ ਸਥਿਤੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ਸਥਿਤੀ ਬਾਰ ਅਸਮਰੱਥ ਬਣਾਓ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਉਣ ਜਾਂ ਸਿਸਟਮ ਆਈਕਨਾਂ ਨੂੰ ਜੋੜਨ ਅਤੇ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ਸਥਿਤੀ ਪੱਟੀ ਬਣਨ ਦਿਓ"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"ਵਿਵਿਧ"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ae06e33..718b008 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Można zainstalować skrypty, by zawartość aplikacji była łatwiej dostępna."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Obserwowanie wpisywanego tekstu"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obejmuje informacje osobiste, takie jak numery kart kredytowych i hasła."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Regulowanie powiększenia ekranu"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Regulowanie poziomu i obszaru powiększenia ekranu."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"działanie jako pasek stanu"</string>
@@ -1535,4 +1537,6 @@
<item quantity="other">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Wybrano <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4d88275..9c1c935 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scripts podem ser instalados para tornar o conteúdo do app mais acessível."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar o texto digitado"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
@@ -1499,4 +1501,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Diversos"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 863b6e8..1986e97 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Poderão ser instalados scripts para tornar o conteúdo da aplicação mais acessível."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar o texto que escreve"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartões de crédito e palavras-passe."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar a ampliação do ecrã"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nível de zoom e o posicionamento do ecrã."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite à aplicação desativar a barra de estado ou adicionar e remover ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser apresentada na barra de estado"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4d88275..fadf3c8 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Scripts podem ser instalados para tornar o conteúdo do app mais acessível."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observar o texto digitado"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 2e06a0e..5c15597 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -253,6 +253,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Pot fi instalate scripturi pentru a face conținutul aplicațiilor mai accesibil."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Remarcă textul pe care îl introduceți"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlați mărirea afișajului"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlați nivelul de zoom și poziționarea afișajului."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"dezactivare sau modificare bare de stare"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"să fie bara de stare"</string>
@@ -358,7 +360,7 @@
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"citeşte starea și identitatea telefonului"</string>
- <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanţă conectat printr-un apel."</string>
+ <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Permite aplicației să acceseze funcţiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"împiedicarea computerului tablet PC să intre în repaus"</string>
<string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"împiedică intrarea televizorului în stare de inactivitate"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"împiedicare intrare telefon în repaus"</string>
@@ -398,9 +400,9 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar televizorul dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Permite aplicației să primească pachetele trimise către toate dispozitivele dintr-o rețea Wi-Fi, utilizând adrese cu difuzare multiplă, nu doar telefonul dvs. Această funcție utilizează mai multă energie decât modul fără difuzare multiplă."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"accesează setările Bluetooth"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se împerecheze cu dispozitive la distanţă."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Permite aplicației să configureze tableta Bluetooth locală, să descopere și să se împerecheze cu dispozitive la distanță."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Permite aplicației să configureze televizorul Bluetooth local, precum și să descopere și să se asocieze cu dispozitive la distanță."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanţă."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanță."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"se conectează și se deconectează de la WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Permite aplicației să stabilească dacă o rețea WiMAX este activată și să vadă informațiile cu privire la toate reţelele WiMAX conectate."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"schimbaţi starea WiMAX"</string>
@@ -1517,4 +1519,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selectat</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8a64091..d826904 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Могут быть установлены дополнительные скрипты."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Обрабатывать набираемый текст"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"В том числе личные данные, например номера кредитных карт и пароли."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управлять масштабом изображения"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управлять позиционированием и размером изображения на экране."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Отключение/изменение строки состояния"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Замена строки состояния"</string>
@@ -1535,4 +1537,6 @@
<item quantity="many">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index c6f0d08..8da80e1 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"යෙදුම් අන්තර්ගතයට ප්රවේශ්යතාවය වැඩිවන ලෙස සකස් කිරීමට ඇතැම් විට ස්ක්රිප්ට් ස්ථාපනය කර ඇත."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ඔබ ටයිප් කළ පෙළ බලන්න"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශන විශාලන මට්ටම පාලනය කිරීම"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කිරීම."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"තත්ව තීරුව අක්රිය කිරීමට හෝ පද්ධති නිරූපක එකතු හෝ ඉවත් කිරීමට යෙදුමට අවසර දේ."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"තත්ත්ව තීරුව බවට පත්වීම"</string>
@@ -1501,4 +1503,5 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"විවිධාකාර"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 0ecb92d..456cfda 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Môže nainštalovať skripty na sprístupnenie obsahu aplikácie."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Sledovať zadávaný text"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledovanie zahŕňa osobné údaje ako sú čísla kreditných kariet a heslá."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ovládanie priblíženia obrazovky"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ovládajte úroveň priblíženia/oddialenia obrazovky a umiestnenie"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vydávanie sa za stavový riadok"</string>
@@ -1535,4 +1537,6 @@
<item quantity="other">Vybrané: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Vybrané: <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index a5c1013..091fe10 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Za boljšo dostopnost vsebine aplikacije je mogoče namestiti skripte."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Opazovati besedilo, ki ga natipkate"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Vključuje osebne podatke, kot so številke kreditnih kartic in gesla."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nadziranje povečave prikaza"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Nadziranje stopnje povečave in položaja prikaza."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogočanje ali spreminjanje vrstice stanja"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"postane vrstica stanja"</string>
@@ -1535,4 +1537,6 @@
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> izbrani</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> izbranih</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 82f8b3b..283c9d3 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skriptet mund të instalohen për ta bërë përmbajtjen e aplikacionit më të qasshme."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Vëzhgojë tekstin që shkruan"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Përfshi të dhënat personale si numrat e kartave të kreditit si dhe fjalëkalimet."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollo zmadhimin e ekranit"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollo nivelin dhe pozicionimin e zmadhimit të ekranit."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"çaktivizo ose modifiko shiritin e statusit"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"të bëhet shiriti i statusit"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> të zgjedhura</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> i zgjedhur</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Të ndryshme"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index bab8944..d484ccb 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -253,6 +253,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Могу да се инсталирају скрипте да би садржај апликација био приступачнији."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Прати текст који уносите"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Обухвата личне податке као што су бројеви кредитних картица и лозинке."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управљај увећањем приказа"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управља нивоом зумирања приказа и одређивањем положаја."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"онемогућавање или измена статусне траке"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"функционисање као статусна трака"</string>
@@ -1517,4 +1519,6 @@
<item quantity="few">Изабране су <xliff:g id="COUNT_1">%1$d</xliff:g> ставке</item>
<item quantity="other">Изабрано је <xliff:g id="COUNT_1">%1$d</xliff:g> ставки</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3909c6b..87e2d59 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Skript kan installeras för att göra appens innehåll tillgängligare."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Observera text som du skriver"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omfattar personuppgifter som kreditkortsnummer och lösenord."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Styr skärmförstoringen"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Styr skärmens zoomnivå och positionering."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"visas i statusfältet"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> har valts</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> har valts</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c9cdd8d..31f10b5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Hati zinaweza kusakinishwa ili kuyafanya maudhui ya programu kufikiwa zaidi."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Angalia maandishi unayoyacharaza"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inajumuisha data binafsi kama vile nambari za kadi ya mkopo na manenosiri."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Dhibiti ukuzaji wa onyesho"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Dhibiti kiwango cha kukuza na nafasi cha onyesho."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zima au rekebisha mwambaa hali"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa ikoni za mfumo."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"kuwa sehemu ya arifa"</string>
@@ -1501,4 +1503,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> vimechaguliwa</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kimechaguliwa</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 94e9c4e..9c45c12 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -22,10 +22,6 @@
<dimen name="thumbnail_width">360dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
<dimen name="thumbnail_height">360dp</dimen>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">5</integer>
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">56dip</dimen>
<!-- Vertical padding around action bar icons. -->
@@ -88,7 +84,7 @@
<!-- Size of the generic status lines keyguard's status view -->
<dimen name="kg_status_line_font_size">16sp</dimen>
- <!-- Top margin for the clock view -->
+ <!-- Top margin for the clock view -->
<dimen name="kg_clock_top_margin">0dp</dimen>
<!-- Size of margin on the right of keyguard's status view -->
diff --git a/core/res/res/values-sw720dp/config.xml b/core/res/res/values-sw720dp/config.xml
index 9792835..1f5791a 100644
--- a/core/res/res/values-sw720dp/config.xml
+++ b/core/res/res/values-sw720dp/config.xml
@@ -19,4 +19,7 @@
used for picking activities to handle an intent. -->
<integer name="config_maxResolverActivityColumns">4</integer>
+ <!-- Enable cascading submenus. -->
+ <bool name="config_enableCascadingSubmenus">true</bool>
+
</resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 67b85583..72ef302 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"பயன்பாட்டு உள்ளடக்கத்தை மேலும் எளிதாக அணுகக்கூடியதாக்க ஸ்கிரிப்ட்கள் நிறுவப்படலாம்."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"நீங்கள் தட்டச்சு செய்யும் உரையைக் கவனிக்கும்"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"கிரெடிட் கார்டு எண்கள் மற்றும் கடவுச்சொற்கள் போன்ற தனிப்பட்ட தகவலும் உள்ளடங்கும்."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"திரையின் உருப்பெருக்கத்தைக் கட்டுப்படுத்துதல்"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"திரையின் ஜூம் அளவையும் நிலையையும் கட்டுப்படுத்தலாம்."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"நிலைப் பட்டியை முடக்குதல் அல்லது மாற்றுதல்"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"நிலைப் பட்டியை முடக்க அல்லது முறைமையில் ஐகான்களைச் சேர்க்க மற்றும் அகற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"நிலைப் பட்டியில் இருக்கும்"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டன</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டது</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"இதர அமைப்பு"</string>
</resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 97789e1..8d16646 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"అనువర్తన కంటెంట్కు మరింత సులభ ప్రాప్యత సౌలభ్యం అందించడానికి స్క్రిప్ట్లు ఇన్స్టాల్ చేయబడవచ్చు."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"మీరు టైప్ చేస్తున్న వచనాన్ని పరిశీలిస్తుంది"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"క్రెడిట్ కార్డు నంబర్లు మరియు పాస్వర్డ్ల వంటి వ్యక్తిగత డేటాను కలిగి ఉంటుంది."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"డిస్ప్లే మాగ్నిఫికేషన్ను నియంత్రించండి"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"డిస్ప్లే జూమ్ స్థాయి మరియు స్థానాన్ని నియంత్రిస్తుంది."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"స్థితి బార్ను నిలిపివేయడం లేదా సవరించడం"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"స్థితి బార్ను నిలిపివేయడానికి లేదా సిస్టమ్ చిహ్నాలను జోడించడానికి మరియు తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"స్థితి పట్టీగా ఉండటం"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఎంచుకోబడ్డాయి</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"ఇతరాలు"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 013da3d..bc59123 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"อาจติดตั้งสคริปต์เพื่อทำให้สามารถเข้าถึงเนื้อหาแอปได้ง่ายขึ้น"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"สังเกตข้อความที่คุณพิมพ์"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"รวมถึงข้อมูลส่วนบุคคล เช่น หมายเลขบัตรเครดิตและรหัสผ่าน"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ควบคุมการขยายการแสดงผล"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ควบคุมระดับการซูมและการวางตำแหน่งของการแสดงผล"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ปิดการใช้งานหรือแก้ไขแถบสถานะ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"เป็นแถบสถานะ"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">เลือกไว้ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item>
<item quantity="one">เลือกไว้ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 8c6c37d..41cb1d6 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Maaaring mag-install ng mga script upang gawing mas naa-access ang nilalaman ng app."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Obserbahan ang tekstong tina-type mo"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"May kasamang personal na data tulad ng mga numero ng credit card at password."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolin ang pag-magnify ng display"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolin ang antas ng pag-zoom at pagpoposisyon ng display."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"huwag paganahin o baguhin ang status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pinapayagan ang app na huwag paganahin ang status bar o magdagdag at mag-alis ng mga icon ng system."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"maging status bar"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 20f6191..f68b3a8 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Uygulamanın erişilebilirliğini artırmak için komut dosyaları yüklenebilir."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Yazdığınız metni izleme"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredi kartı ve şifre gibi kişisel bilgiler içerir."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran büyütecini kontrol et"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranın yakınlaştırma seviyesini ve konumunu kontrol edin."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"durum çubuğunda olma"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> öğe seçildi</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> öğe seçildi</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 871153f..4173021 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -254,6 +254,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Можуть установлюватися сценарії, щоб зробити вміст програми доступнішим."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Обробляти текст, який ви вводите"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включає особисті дані, як-от номери кредитних карток і паролі."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролювати збільшення екрана"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролювати масштаб і розташування екрана."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"вимикати чи змін. рядок стану"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"відображатися як рядок стану"</string>
@@ -1535,4 +1537,5 @@
<item quantity="many">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Інше"</string>
</resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 175a840..a6e7cf8 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"ایپ کا مواد مزید قابل رسائی بنانے کیلئے اسکرپٹس کو انسٹال کیا جا سکتا ہے۔"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"آپکے ٹائپ کردہ متن کا مشاہدہ کرنے کی"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اس میں ذاتی ڈیٹا جیسے کریڈٹ کارڈ نمبرز اور پاس ورڈز شامل ہیں۔"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ڈسپلے بڑا کرنے کے عمل کو کنٹرول کریں"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ڈسپلے کے زوم کی سطح اور پوزیشن کو کنٹرول کریں۔"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"بطور اسٹیٹس بار کام لیں"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> منتخب کردہ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> منتخب کردہ</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 278ef6d..5cadf34 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Qo‘shimcha skriptlar o‘rnatilishi mumkin."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Kiritilayotgan matnni kuzatadi"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bunga kredit karta raqamlari va parollar kabi shaxsiy ma’lumotlar kiradi."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekranni kattalashtirishni boshqarish"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranni kattalashtirish darajasi va joylashuvini boshqaradi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"holat panelini o‘zgartirish yoki o‘chirish"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"holat qatorida ko‘rinishi"</string>
@@ -664,7 +666,7 @@
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"SIM karta yo‘q"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Planshetingizga SIM karta yo‘q."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Televizorda SIM karta yo‘q."</string>
- <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Telefoningizga SIM karta yo‘q."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Telefoningizda SIM karta yo‘q."</string>
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"SIM kartani soling."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM karta solinmagan yoki uni o‘qib bo‘lmaydi. SIM kartani soling."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Foydalanib bo‘lmaydigan SIM karta."</string>
@@ -1499,4 +1501,6 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta tanlandi</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta tanlandi</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 439fe25..26d8ca4 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Tập lệnh có thể được cài đặt để làm cho nội dung ứng dụng dễ truy cập hơn."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Xem nội dung bạn nhập"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bao gồm dữ liệu cá nhân chẳng hạn như số thẻ tín dụng và mật khẩu."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kiểm soát thu phóng màn hình"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kiểm soát vị trí và mức thu phóng của màn hình."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"vô hiệu hóa hoặc sửa đổi thanh trạng thái"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"trở thành thanh trạng thái"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other">Đã chọn <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Đã chọn <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"Khác"</string>
</resources>
diff --git a/core/res/res/values-w360dp/dimens.xml b/core/res/res/values-w360dp/dimens.xml
deleted file mode 100644
index 0f5d656..0000000
--- a/core/res/res/values-w360dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">3</integer>
-</resources>
diff --git a/core/res/res/values-w500dp/dimens.xml b/core/res/res/values-w500dp/dimens.xml
deleted file mode 100644
index 68841ca..0000000
--- a/core/res/res/values-w500dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">4</integer>
-</resources>
diff --git a/core/res/res/values-w600dp/dimens.xml b/core/res/res/values-w600dp/dimens.xml
deleted file mode 100644
index 83c45b5..0000000
--- a/core/res/res/values-w600dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">5</integer>
-</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6e79f3d..e43431c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"安装脚本以方便访问应用的内容。"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"监测您输入的文字"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包含个人数据,例如信用卡号和密码。"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制显示内容放大功能"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制显示内容的缩放级别和位置。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"用作状态栏"</string>
@@ -376,10 +378,10 @@
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"允许应用更改平板电脑的时区。"</string>
<string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"允许应用更改电视的时区。"</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"允许应用更改手机的时区。"</string>
- <string name="permlab_getAccounts" msgid="1086795467760122114">"查找设备上的帐户"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"允许该应用获取平板电脑已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"允许应用获取电视已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允许该应用获取手机已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
+ <string name="permlab_getAccounts" msgid="1086795467760122114">"查找设备上的帐号"</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"允许该应用获取平板电脑已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"允许应用获取电视已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允许该应用获取手机已知的帐号列表,其中可能包括由已安装的应用创建的所有帐号。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"查看网络连接"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"允许该应用查看网络连接的相关信息,例如存在和连接的网络。"</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"拥有完全的网络访问权限"</string>
@@ -436,11 +438,11 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指纹图标"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐户的同步设置。例如,此权限可确定“联系人”应用是否与某个帐户同步。"</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐号的同步设置。例如,此权限可确定“联系人”应用是否与某个帐号同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允许该应用修改某个帐户的同步设置。例如,此权限可用于在“联系人”应用与某个帐户之间启用同步。"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"允许该应用修改某个帐号的同步设置。例如,此权限可用于在“联系人”应用与某个帐号之间启用同步。"</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"读取同步统计信息"</string>
- <string name="permdesc_readSyncStats" msgid="1510143761757606156">"允许该应用读取某个帐户的同步统计信息,包括同步活动历史记录和同步数据量。"</string>
+ <string name="permdesc_readSyncStats" msgid="1510143761757606156">"允许该应用读取某个帐号的同步统计信息,包括同步活动历史记录和同步数据量。"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"读取您的USB存储设备中的内容"</string>
<string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"读取您的SD卡中的内容"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"允许应用读取您USB存储设备中的内容。"</string>
@@ -696,9 +698,9 @@
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>秒后重试。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘记了图案?"</string>
- <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"帐户解锁"</string>
+ <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"帐号解锁"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"图案尝试次数过多"</string>
- <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"要解除锁定,请使用您的Google帐户登录。"</string>
+ <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"要解除锁定,请使用您的Google帐号登录。"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"用户名(电子邮件)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"密码"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"登录"</string>
@@ -1088,13 +1090,13 @@
<string name="ime_action_default" msgid="2840921885558045721">"执行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"拨打电话\n<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"创建电话号码为\n<xliff:g id="NUMBER">%s</xliff:g> 的联系人"</string>
- <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐户。"</string>
+ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"以下一个或多个应用请求获得相应权限,以便在当前和以后访问您的帐号。"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"您是否同意此请求?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"访问权限请求"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"权限请求"</string>
- <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用对帐户 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string>
+ <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"应用对帐号 <xliff:g id="ACCOUNT">%s</xliff:g>\n 提出权限请求。"</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"您目前是在工作资料之外使用此应用"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"您目前是在工作资料内使用此应用"</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"输入法"</string>
@@ -1143,13 +1145,13 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"是"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"否"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"超出删除限制"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"帐户 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"帐号 <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> 在进行“<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>”同步时删除了 <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> 项内容。您要如何处理这些删除的内容?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"删除这些内容"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"撤消删除"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"目前不进行任何操作"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"选择帐户"</string>
- <string name="add_account_label" msgid="2935267344849993553">"添加帐户"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"添加帐户"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"选择帐号"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"添加帐号"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"添加帐号"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"增大"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"减小"</string>
<string name="number_picker_increment_scroll_mode" msgid="3073101067441638428">"触摸 <xliff:g id="VALUE">%s</xliff:g> 次并按住。"</string>
@@ -1276,13 +1278,13 @@
<string name="kg_invalid_puk" msgid="3638289409676051243">"请重新输入正确的PUK码。如果尝试错误次数过多,SIM卡将永久停用。"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN码不匹配"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"图案尝试次数过多"</string>
- <string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的Google帐户。"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"要解锁,请登录您的Google帐号。"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"用户名(电子邮件地址)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"密码"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"登录"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"用户名或密码无效。"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘记了用户名或密码?\n请访问 "<b>"google.com/accounts/recovery"</b>"。"</string>
- <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐户…"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐号…"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经<xliff:g id="NUMBER_0">%1$d</xliff:g>次输错了PIN码。\n\n请在<xliff:g id="NUMBER_1">%2$d</xliff:g>秒后重试。"</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
@@ -1292,9 +1294,9 @@
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁平板电脑。平板电脑现在将恢复为出厂默认设置。"</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁电视。电视现在将恢复为出厂默认设置。"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"您已经<xliff:g id="NUMBER">%d</xliff:g>次错误地尝试解锁手机。手机现在将恢复为出厂默认设置。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件帐户解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用电子邮件帐号解锁电视。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要将音量调高到推荐水平以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">已选择 <xliff:g id="COUNT_1">%1$d</xliff:g> 项</item>
<item quantity="one">已选择 <xliff:g id="COUNT_0">%1$d</xliff:g> 项</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index ddafe6d..1b69a02 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"可能會安裝程式碼,使應用程式內容更易於存取。"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"記錄您輸入的文字"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制顯示屏的放大功能"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制顯示屏的縮放程度和位置。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"成為狀態列"</string>
@@ -1499,4 +1501,5 @@
<item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
<item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
</plurals>
+ <string name="default_notification_topic_label" msgid="227586145791870829">"其他"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1163137..0aa8cea 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"可能會安裝程式碼,使應用程式內容更易於存取。"</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"記錄您輸入的文字"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控管顯示畫面放大功能"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控管顯示畫面的縮放等級和位置。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"以狀態列顯示"</string>
@@ -1499,4 +1501,6 @@
<item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
<item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9557f58..5fbe784 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -252,6 +252,8 @@
<string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"Amaskripthi angase afakwe ukwenza okuqukethwe kohlelo lokusebenza kufinyeleleke kakhulu."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"Qapha umbhalo owuthayiphayo"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kufaka phakathi idatha yomuntu siqu efana nezinombolo zekhadi lesikweletu namaphasiwedi."</string>
+ <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Lawula ukulungiswa kwesibonisi"</string>
+ <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Lawula ileveli yokusondeza yesibonisi nendawo."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"khubaza noma guqula ibha yomumo"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"yiba yibha yesimo"</string>
@@ -1499,4 +1501,6 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
</plurals>
+ <!-- no translation found for default_notification_topic_label (227586145791870829) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 33c41ef..a6a4564 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -364,6 +364,11 @@
<item>@color/search_url_text_material_light</item>
</array>
+ <array name="preloaded_freeform_multi_window_drawables">
+ <item>@drawable/decor_maximize_button_dark</item>
+ <item>@drawable/decor_maximize_button_light</item>
+ </array>
+
<!-- Used in LocalePicker -->
<string-array translatable="false" name="special_locale_codes">
<!-- http://b/17150708 - ensure that the list of languages says "Arabic"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d7dd3ec..f9f8162 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -942,6 +942,10 @@
<!-- Layout of the TextView item that will populate the suggestion popup window. -->
<attr name="textEditSuggestionItemLayout" format="reference" />
+ <!-- Layout of the container of the suggestion popup window. -->
+ <attr name="textEditSuggestionContainerLayout" format="reference" />
+ <!-- Text appearance of the focused words to be replaced by suggested word. -->
+ <attr name="textEditSuggestionHighlightStyle" format="reference" />
<!-- Theme to use for dialogs spawned from this theme. -->
<attr name="dialogTheme" format="reference" />
@@ -3242,6 +3246,14 @@
</p>
-->
<attr name="canRequestFilterKeyEvents" format="boolean" />
+ <!-- Attribute whether the accessibility service wants to be able to control
+ display magnification.
+ <p>
+ Required to allow setting the {@link android.accessibilityservice
+ #AccessibilityServiceInfo#FLAG_CAN_CONTROL_MAGNIFICATION} flag.
+ </p>
+ -->
+ <attr name="canControlMagnification" format="boolean" />
<!-- Short description of the accessibility serivce purpose or behavior.-->
<attr name="description" />
</declare-styleable>
@@ -4240,7 +4252,8 @@
<attr name="autoLink" />
<!-- If set to false, keeps the movement method from being set
to the link movement method even if autoLink causes links
- to be found. -->
+ to be found or the input text contains a
+ {@link android.text.style.ClickableSpan ClickableSpan}. -->
<attr name="linksClickable" format="boolean" />
<!-- If set, specifies that this TextView has a numeric input method.
The default is false.
@@ -4402,6 +4415,10 @@
<!-- Layout of the TextView item that will populate the suggestion popup window. -->
<attr name="textEditSuggestionItemLayout" />
+ <!-- Layout of the container of the suggestion popup window. -->
+ <attr name="textEditSuggestionContainerLayout" />
+ <!-- Style of the highlighted string in the suggestion popup window. -->
+ <attr name="textEditSuggestionHighlightStyle" />
<!-- Reference to a drawable that will be drawn under the insertion cursor. -->
@@ -7623,6 +7640,8 @@
<attr name="pointerIconHand" format="reference"/>
<!-- Reference to a pointer drawable with STYLE_HELP -->
<attr name="pointerIconHelp" format="reference"/>
+ <!-- Reference to a pointer drawable with STYLE_WAIT -->
+ <attr name="pointerIconWait" format="reference"/>
<!-- Reference to a pointer drawable with STYLE_CELL -->
<attr name="pointerIconCell" format="reference"/>
<!-- Reference to a pointer drawable with STYLE_CROSSHAIR -->
@@ -7714,6 +7733,7 @@
<declare-styleable name="PreferenceFragment">
<!-- The layout for the PreferenceFragment. This should rarely need to be changed. -->
<attr name="layout" />
+ <attr name="divider" />
</declare-styleable>
<!-- Base attributes available to PreferenceActivity. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 184f2ab..67933cd 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -793,6 +793,10 @@
physical screen size has changed such as switching to an external
display. -->
<flag name="smallestScreenSize" value="0x0800" />
+ <!-- The display density has changed. This might be caused by the user
+ specifying a different display scale, or it might be caused by a
+ different display being activated. -->
+ <flag name="density" value="0x1000" />
<!-- The layout direction has changed. For example going from LTR to RTL. -->
<flag name="layoutDirection" value="0x2000" />
<!-- The font scaling factor has changed, that is the user has
@@ -1283,6 +1287,7 @@
<attr name="multiArch" />
<attr name="extractNativeLibs" />
<attr name="forceDeviceEncrypted" format="boolean" />
+ <attr name="encryptionAware" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 7c63950..457131a 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -25,8 +25,4 @@
<bool name="show_ongoing_ime_switcher">true</bool>
<bool name="action_bar_expanded_action_views_exclusive">true</bool>
<bool name="target_honeycomb_needs_options_menu">true</bool>
-
- <!-- Whether to allow vertically stacked button bars. This is disabled for
- configurations with a small (e.g. less than 320dp) screen height. -->
- <bool name="allow_stacked_button_bar">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 76f7062..d9e0472 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -411,7 +411,7 @@
<bool translatable="false" name="config_wifi_revert_country_code_on_cellular_loss">false</bool>
<!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
- <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">false</bool>
+ <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">true</bool>
<!-- Integer specifying the basic autojoin parameters -->
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
@@ -1091,6 +1091,14 @@
This feature should be disabled for most devices. -->
<integer name="config_virtualKeyQuietTimeMillis">0</integer>
+ <!-- A list of potential packages, in priority order, that may contain an
+ ephemeral resolver. Each package will be be queried for a component
+ that has been granted the PACKAGE_EPHEMERAL_AGENT permission.
+ This may be empty if ephemeral apps are not supported. -->
+ <string-array name="config_ephemeralResolverPackage" translatable="false">
+ <!-- Add packages here -->
+ </string-array>
+
<!-- Component name of the default wallpaper. This will be ImageWallpaper if not
specified -->
<string name="default_wallpaper_component" translatable="false">@null</string>
@@ -1530,8 +1538,8 @@
<!-- If the time difference is greater than this threshold in milliseconds,
then update the time. -->
<integer name="config_ntpThreshold">5000</integer>
- <!-- Timeout to wait for NTP server response. -->
- <integer name="config_ntpTimeout">20000</integer>
+ <!-- Timeout to wait for NTP server response in milliseconds. -->
+ <integer name="config_ntpTimeout">5000</integer>
<!-- Default network policy warning threshold, in megabytes. -->
<integer name="config_networkPolicyDefaultWarning">2048</integer>
@@ -1938,9 +1946,9 @@
See {@link com.android.server.notification.NotificationSignalExtractor} -->
<string-array name="config_notificationSignalExtractors">
<item>com.android.server.notification.ValidateNotificationPeople</item>
- <item>com.android.server.notification.PackagePriorityExtractor</item>
+ <item>com.android.server.notification.TopicPriorityExtractor</item>
<item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
- <item>com.android.server.notification.PackageVisibilityExtractor</item>
+ <item>com.android.server.notification.TopicVisibilityExtractor</item>
</string-array>
<!-- Flag indicating that this device does not rotate and will always remain in its default
@@ -2369,4 +2377,11 @@
<!-- The BT name of the keyboard packaged with the device. If this is defined, SystemUI will
automatically try to pair with it when the device exits tablet mode. -->
<string translatable="false" name="config_packagedKeyboardName"></string>
+
+ <!-- The device supports freeform window management. Windows have title bars and can be moved
+ and resized. If you set this to true, you also need to add
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT feature to your device specification.
+ The duplication is necessary, because this information is used before the features are
+ available to the system.-->
+ <bool name="config_freeformWindowManagement">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 28756f5..01daf26 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -25,10 +25,7 @@
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="max_action_buttons">2</integer>
+
<dimen name="toast_y_offset">64dip</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_height">24dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a348767..7f8acd3 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -23,12 +23,14 @@
<item type="id" name="empty" />
<item type="id" name="hint" />
<item type="id" name="icon" />
+ <item type="id" name="icon_frame" />
<item type="id" name="icon_badge" />
<item type="id" name="icon1" />
<item type="id" name="icon2" />
<item type="id" name="input" />
<item type="id" name="left_icon" />
<item type="id" name="list" />
+ <item type="id" name="list_container" />
<item type="id" name="menu" />
<item type="id" name="message" />
<item type="id" name="primary" />
@@ -48,6 +50,7 @@
<item type="id" name="lock_screen" />
<item type="id" name="edit" />
<item type="id" name="widget_frame" />
+ <item type="id" name="switch_widget" />
<item type="id" name="button1" />
<item type="id" name="button2" />
<item type="id" name="button3" />
@@ -122,4 +125,6 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->
<item type="id" name="accessibilityActionContextClick" />
+
+ <item type="id" name="remote_input_tag" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 037f1c4..b6b2e20 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2679,6 +2679,10 @@
<public type="attr" name="contextPopupMenuStyle" />
<public type="attr" name="textAppearancePopupMenuHeader" />
<public type="attr" name="windowBackgroundFallback" />
+ <public type="attr" name="forceDeviceEncrypted" />
+ <public type="attr" name="encryptionAware" />
+ <public type="attr" name="preferenceFragmentStyle" />
+ <public type="attr" name="canControlMagnification" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2699,5 +2703,8 @@
<public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
<public type="id" name="accessibilityActionSetProgress" />
+ <public type="id" name="icon_frame" />
+ <public type="id" name="list_container" />
+ <public type="id" name="switch_widget" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index faa76f2..8a00294 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -613,6 +613,12 @@
<string name="capability_desc_canRequestFilterKeyEvents">Includes personal data such as credit
card numbers and passwords.</string>
+ <!-- Title for the capability of an accessibility service to control display magnification. -->
+ <string name="capability_title_canControlMagnification">Control display magnification</string>
+ <!-- Description for the capability of an accessibility service to control display magnification. -->
+ <string name="capability_desc_canControlMagnification">Control the display\'s zoom level and
+ positioning.</string>
+
<!-- Permissions -->
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3617,6 +3623,8 @@
<string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
<!-- Message shown when switching to a user [CHAR LIMIT=none] -->
<string name="user_switching_message">Switching to <xliff:g id="name" example="Bob">%1$s</xliff:g>\u2026</string>
+ <!-- Message when logging out a user on a split user system -->
+ <string name="user_logging_out_message">Logging out <xliff:g id="name" example="Bob">%1$s</xliff:g>\u2026</string>
<!-- Default name of the owner user [CHAR LIMIT=20] -->
<string name="owner_name" msgid="3879126011135546571">Owner</string>
<!-- Error message title [CHAR LIMIT=35] -->
@@ -3926,6 +3934,10 @@
<!-- Lock-to-app unlock password string -->
<string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
+ <!-- Multi-Window strings -->
+ <!-- Warning message when a non-resizeble tasks is docked. -->
+ <string name="dock_non_resizeble_text">App is not resizeable, scroll it with two fingers.</string>
+
<!-- Notification shown when device owner silently installs a package [CHAR LIMIT=NONE] -->
<string name="package_installed_device_owner">Installed by your administrator</string>
<!-- Notification shown when device owner silently updates a package [CHAR LIMIT=NONE] -->
@@ -4066,4 +4078,6 @@
<item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item>
<item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
</plurals>
+
+ <string name="default_notification_topic_label">Miscellaneous</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b831df8..e6f279d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -496,6 +496,8 @@
<item name="textEditSidePasteWindowLayout">?attr/textEditSidePasteWindowLayout</item>
<item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item>
<item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
+ <item name="textEditSuggestionContainerLayout">?attr/textEditSuggestionContainerLayout</item>
+ <item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
<item name="textCursorDrawable">?attr/textCursorDrawable</item>
<item name="breakStrategy">high_quality</item>
<item name="hyphenationFrequency">normal</item>
@@ -954,10 +956,10 @@
<item name="dividerPadding">6dip</item>
</style>
- <style name="TextAppearance.SuggestionHighlight">
- <item name="textSize">18sp</item>
- <item name="textColor">@color/suggestion_highlight_text</item>
- </style>
+ <style name="TextAppearance.SuggestionHighlight">
+ <item name="textSize">18sp</item>
+ <item name="textColor">@color/suggestion_highlight_text</item>
+ </style>
<!-- Preference Styles -->
@@ -1339,6 +1341,7 @@
<item name="pointerIconHand">@drawable/pointer_hand_icon</item>
<item name="pointerIconContextMenu">@drawable/pointer_context_menu_icon</item>
<item name="pointerIconHelp">@drawable/pointer_help_icon</item>
+ <item name="pointerIconWait">@drawable/pointer_wait_icon</item>
<item name="pointerIconCell">@drawable/pointer_cell_icon</item>
<item name="pointerIconCrosshair">@drawable/pointer_crosshair_icon</item>
<item name="pointerIconText">@drawable/pointer_text_icon</item>
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 6861069..841afd8 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -1176,4 +1176,27 @@
<style name="Widget.Holo.Light.FastScroll" parent="Widget.Holo.FastScroll" />
+ <style name="Widget.Holo.SuggestionItem" parent="@android:attr/textAppearanceMedium">
+ <item name="background">@color/white</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">wrap_content</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textSize">18sp</item>
+ <item name="textColor">@color/black</item>
+ </style>
+
+ <style name="TextAppearance.Holo.SuggestionHighlight" parent="TextAppearance.SuggestionHighlight" />
+
+ <style name="Widget.Holo.SuggestionButton" parent="Widget.Holo.SuggestionItem">
+ <item name="background">#E9E9E9</item>
+ <item name="textColor">@color/black</item>
+ </style>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 58640eb..adcb79b 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -40,6 +40,7 @@
<item name="layout">@layout/preference_list_fragment_material</item>
<item name="paddingStart">@dimen/preference_fragment_padding_side_material</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
+ <item name="divider">?attr/listDivider</item>
</style>
<style name="PreferenceActivity.Material">
@@ -987,6 +988,47 @@
<item name="contentDescription">@string/media_route_button_content_description</item>
</style>
+ <style name="Widget.Material.SuggestionItem" parent="@android:style/TextAppearance.Material.Body1">
+ <item name="background">@color/white</item>
+ <item name="alpha">.87</item>
+ <item name="textColor">@color/black</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">48dip</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textSize">14sp</item>
+ </style>
+
+ <style name="TextAppearance.Material.TextSuggestionHighlight" parent="Widget.Material.SuggestionItem">
+ <item name="textColor">#009688</item>
+ </style>
+
+ <style name="Widget.Material.SuggestionButton" parent="@android:style/TextAppearance.Material.Button">
+ <item name="background">@color/white</item>
+ <item name="alpha">.87</item>
+ <item name="textColor">#009688</item>
+ <item name="drawablePadding">8dip</item>
+ <item name="ellipsize">marquee</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="layout_gravity">start|center_vertical</item>
+ <item name="layout_height">48dip</item>
+ <item name="layout_width">match_parent</item>
+ <item name="paddingBottom">8dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingTop">8dip</item>
+ <item name="singleLine">true</item>
+ <item name="textAllCaps">true</item>
+ <item name="textSize">14sp</item>
+ </style>
+
<!-- Light widget styles -->
<style name="Widget.Material.Light" parent="Widget.Material"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 80e5668..8cf1134 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -162,7 +162,7 @@
<java-symbol type="id" name="submit_area" />
<java-symbol type="id" name="switch_new" />
<java-symbol type="id" name="switch_old" />
- <java-symbol type="id" name="switchWidget" />
+ <java-symbol type="id" name="switch_widget" />
<java-symbol type="id" name="text" />
<java-symbol type="id" name="time" />
<java-symbol type="id" name="time_current" />
@@ -314,6 +314,7 @@
<java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
+ <java-symbol type="bool" name="config_freeformWindowManagement" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -404,7 +405,6 @@
<java-symbol type="integer" name="db_connection_pool_size" />
<java-symbol type="integer" name="db_journal_size_limit" />
<java-symbol type="integer" name="db_wal_autocheckpoint" />
- <java-symbol type="integer" name="max_action_buttons" />
<java-symbol type="integer" name="config_soundEffectVolumeDb" />
<java-symbol type="integer" name="config_lockSoundVolumeDb" />
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
@@ -567,6 +567,8 @@
<java-symbol type="string" name="capability_desc_canRequestFilterKeyEvents" />
<java-symbol type="string" name="capability_title_canRequestTouchExploration" />
<java-symbol type="string" name="capability_title_canRetrieveWindowContent" />
+ <java-symbol type="string" name="capability_desc_canControlMagnification" />
+ <java-symbol type="string" name="capability_title_canControlMagnification" />
<java-symbol type="string" name="cfTemplateForwarded" />
<java-symbol type="string" name="cfTemplateForwardedTime" />
<java-symbol type="string" name="cfTemplateNotForwarded" />
@@ -606,6 +608,7 @@
<java-symbol type="string" name="display_manager_overlay_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
+ <java-symbol type="string" name="dock_non_resizeble_text" />
<java-symbol type="string" name="double_tap_toast" />
<java-symbol type="string" name="durationDays" />
<java-symbol type="string" name="durationDayHours" />
@@ -630,6 +633,7 @@
<java-symbol type="string" name="widget_default_package_name" />
<java-symbol type="string" name="widget_default_class_name" />
<java-symbol type="string" name="emergency_calls_only" />
+ <java-symbol type="array" name="config_ephemeralResolverPackage" />
<java-symbol type="string" name="enable_accessibility_canceled" />
<java-symbol type="string" name="eventTypeAnniversary" />
<java-symbol type="string" name="eventTypeBirthday" />
@@ -933,6 +937,7 @@
<java-symbol type="string" name="upload_file" />
<java-symbol type="string" name="user_switched" />
<java-symbol type="string" name="user_switching_message" />
+ <java-symbol type="string" name="user_logging_out_message" />
<java-symbol type="string" name="volume_alarm" />
<java-symbol type="string" name="volume_icon_description_bluetooth" />
<java-symbol type="string" name="volume_icon_description_incall" />
@@ -1123,6 +1128,7 @@
<java-symbol type="array" name="networkAttributes" />
<java-symbol type="array" name="preloaded_color_state_lists" />
<java-symbol type="array" name="preloaded_drawables" />
+ <java-symbol type="array" name="preloaded_freeform_multi_window_drawables" />
<java-symbol type="array" name="sim_colors" />
<java-symbol type="array" name="special_locale_codes" />
<java-symbol type="array" name="special_locale_names" />
@@ -1959,9 +1965,9 @@
<java-symbol type="id" name="maximize_window" />
<java-symbol type="id" name="close_window" />
<java-symbol type="id" name="client_decor_placeholder" />
- <java-symbol type="layout" name="non_client_decor_light" />
- <java-symbol type="layout" name="non_client_decor_dark" />
- <java-symbol type="drawable" name="non_client_decor_title_focused" />
+ <java-symbol type="layout" name="decor_caption_light" />
+ <java-symbol type="layout" name="decor_caption_dark" />
+ <java-symbol type="drawable" name="decor_caption_title_focused" />
<!-- From TelephonyProvider -->
<java-symbol type="xml" name="apns" />
@@ -2303,6 +2309,9 @@
<java-symbol type="string" name="notification_inbox_ellipsis" />
<java-symbol type="bool" name="config_mainBuiltInDisplayIsRound" />
+ <java-symbol type="id" name="actions_container" />
+ <java-symbol type="id" name="remote_input_tag" />
+
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
<java-symbol type="string" name="ext_media_status_removed" />
<java-symbol type="string" name="ext_media_status_unmounted" />
@@ -2322,7 +2331,6 @@
<java-symbol type="string" name="lockscreen_access_pattern_area" />
- <java-symbol type="bool" name="allow_stacked_button_bar" />
<java-symbol type="bool" name="config_eap_sim_based_auth_supported" />
<java-symbol type="array" name="config_cell_retries_per_error_code" />
@@ -2338,4 +2346,10 @@
<java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
<java-symbol type="string" name="config_packagedKeyboardName" />
+ <java-symbol type="string" name="default_notification_topic_label" />
+
+ <!-- EditText suggestion popup. -->
+ <java-symbol type="id" name="suggestionContainer" />
+ <java-symbol type="id" name="addToDictionaryButton" />
+ <java-symbol type="id" name="deleteButton" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index c230645..d56674a 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -256,8 +256,6 @@
<item name="textEditNoPasteWindowLayout">@layout/text_edit_no_paste_window</item>
<item name="textEditSidePasteWindowLayout">@layout/text_edit_side_paste_window</item>
<item name="textEditSideNoPasteWindowLayout">@layout/text_edit_side_no_paste_window</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.TextSuggestionsPopupWindow</item>
- <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item</item>
<item name="textCursorDrawable">@null</item>
<!-- Widget styles -->
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index 701d0ef..d9599b3 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -133,6 +133,11 @@
<item name="textAppearanceLargePopupMenu">@style/TextAppearance.Holo.Widget.PopupMenu.Large</item>
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Holo.Widget.PopupMenu.Small</item>
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container</item>
+ <item name="textEditSuggestionHighlightStyle">@style/TextAppearance.Holo.SuggestionHighlight</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Holo.TextSuggestionsPopupWindow</item>
+
<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Holo.Button</item>
@@ -247,7 +252,6 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Holo.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Holo.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_holo_dark</item>
<!-- Widget styles -->
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 59dfc92..a5b8476 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -221,6 +221,8 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item_material</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container_material</item>
<item name="textSuggestionsWindowStyle">@style/Widget.Material.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_material</item>
@@ -580,9 +582,14 @@
<item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
<item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
<item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Material.Light.TextSuggestionsPopupWindow</item>
<item name="textCursorDrawable">@drawable/text_cursor_material</item>
+ <!-- Suggestion window attributes -->
+ <item name="textEditSuggestionItemLayout">@layout/text_edit_suggestion_item_material</item>
+ <item name="textEditSuggestionContainerLayout">@layout/text_edit_suggestion_container_material</item>
+ <item name="textEditSuggestionHighlightStyle">@style/TextAppearance.Material.TextSuggestionHighlight</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Material.Light.TextSuggestionsPopupWindow</item>
+
<!-- Widget styles -->
<item name="absListViewStyle">@style/Widget.Material.Light.AbsListView</item>
<item name="autoCompleteTextViewStyle">@style/Widget.Material.Light.AutoCompleteTextView</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6903b7b..5177836 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,7 +138,8 @@
<activity android:name="android.widget.TextViewActivity"
android:label="TextViewActivity"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf
new file mode 100644
index 0000000..add3f40
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx
new file mode 100644
index 0000000..7038f46
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="BaseChar1"/>
+ <GlyphID id="2" name="BaseChar1_VS1"/>
+ <GlyphID id="3" name="BaseChar1_VS17"/>
+ <GlyphID id="4" name="BaseChar1_VS18"/>
+ <GlyphID id="5" name="BaseChar2"/>
+ <GlyphID id="6" name="BaseChar2_VS2"/>
+ <GlyphID id="7" name="BaseChar2_VS18"/>
+ <GlyphID id="8" name="BaseChar2_VS19"/>
+ <GlyphID id="9" name="BaseChar3"/>
+ <GlyphID id="10" name="BaseChar4_VS3"/>
+ <GlyphID id="11" name="BaseChar4_VS19"/>
+ <GlyphID id="12" name="BaseChar4_VS20"/>
+ <GlyphID id="13" name="BaseChar5"/>
+ <GlyphID id="14" name="BaseChar5_VS1"/>
+ <GlyphID id="15" name="BaseChar5_VS17"/>
+ <GlyphID id="16" name="BaseChar5_VS18"/>
+ <GlyphID id="17" name="BaseChar6"/>
+ <GlyphID id="18" name="BaseChar6_VS2"/>
+ <GlyphID id="19" name="BaseChar6_VS18"/>
+ <GlyphID id="20" name="BaseChar6_VS19"/>
+ <GlyphID id="21" name="BaseChar7"/>
+ <GlyphID id="22" name="BaseChar8_VS3"/>
+ <GlyphID id="23" name="BaseChar8_VS19"/>
+ <GlyphID id="24" name="BaseChar8_VS20"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Wed Sep 9 08:48:07 2015"/>
+ <xMin value="30"/>
+ <yMin value="-200"/>
+ <xMax value="629"/>
+ <yMax value="800"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="659"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="30"/>
+ <xMaxExtent value="629"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="18"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="54"/>
+ <maxPoints value="73"/>
+ <maxContours value="10"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="BaseChar1" width="500" lsb="93"/>
+ <mtx name="BaseChar1_VS1" width="500" lsb="93"/>
+ <mtx name="BaseChar1_VS17" width="500" lsb="93"/>
+ <mtx name="BaseChar1_VS18" width="500" lsb="93"/>
+ <mtx name="BaseChar2" width="500" lsb="93"/>
+ <mtx name="BaseChar2_VS2" width="500" lsb="93"/>
+ <mtx name="BaseChar2_VS18" width="500" lsb="93"/>
+ <mtx name="BaseChar2_VS19" width="500" lsb="93"/>
+ <mtx name="BaseChar3" width="500" lsb="93"/>
+ <mtx name="BaseChar4_VS3" width="500" lsb="93"/>
+ <mtx name="BaseChar4_VS19" width="500" lsb="93"/>
+ <mtx name="BaseChar4_VS20" width="500" lsb="93"/>
+ <mtx name="BaseChar5" width="500" lsb="93"/>
+ <mtx name="BaseChar5_VS1" width="500" lsb="93"/>
+ <mtx name="BaseChar5_VS17" width="500" lsb="93"/>
+ <mtx name="BaseChar5_VS18" width="500" lsb="93"/>
+ <mtx name="BaseChar6" width="500" lsb="93"/>
+ <mtx name="BaseChar6_VS2" width="500" lsb="93"/>
+ <mtx name="BaseChar6_VS18" width="500" lsb="93"/>
+ <mtx name="BaseChar6_VS19" width="500" lsb="93"/>
+ <mtx name="BaseChar7" width="500" lsb="93"/>
+ <mtx name="BaseChar8_VS3" width="500" lsb="93"/>
+ <mtx name="BaseChar8_VS19" width="500" lsb="93"/>
+ <mtx name="BaseChar8_VS20" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="BaseChar1" />
+ <map code="0x0062" name="BaseChar2" />
+ <map code="0x0063" name="BaseChar3" />
+ <!-- No cmap4 entry for BaseChar4 -->
+ <map code="0x1F000" name="BaseChar5" />
+ <map code="0x1F001" name="BaseChar6" />
+ <map code="0x1F002" name="BaseChar7" />
+ <!-- No cmap4 entry for BaseChar8 -->
+ </cmap_format_12>
+ <cmap_format_14 format="14" platformID="0" platEncID="5" length="24" numVarSelectorRecords="3">
+ <map uvs="0xFE00" uv="0x0061" name="BaseChar1_VS1" />
+ <map uvs="0xE0100" uv="0x0061" name="BaseChar1_VS17" />
+ <map uvs="0xE0101" uv="0x0061" name="BaseChar1_VS18" />
+ <map uvs="0xE0102" uv="0x0061" name="None" />
+
+ <map uvs="0xFE01" uv="0x0062" name="BaseChar2_VS2" />
+ <map uvs="0xE0101" uv="0x0062" name="BaseChar2_VS18" />
+ <map uvs="0xE0102" uv="0x0062" name="BaseChar2_VS19" />
+ <map uvs="0xE0103" uv="0x0062" name="None" />
+
+ <map uvs="0xFE02" uv="0x0064" name="BaseChar4_VS3" />
+ <map uvs="0xE0102" uv="0x0064" name="BaseChar4_VS19" />
+ <map uvs="0xE0103" uv="0x0064" name="BaseChar4_VS20" />
+ <!-- There is no default glyph for U+0064 U+E0104 but there is a entry for
+ default UVS entry. hasGlyph should return false in this
+ case. -->
+ <map uvs="0xE0104" uv="0x0064" name="None" />
+
+ <map uvs="0xFE00" uv="0x1F000" name="BaseChar5_VS1" />
+ <map uvs="0xE0100" uv="0x1F000" name="BaseChar5_VS17" />
+ <map uvs="0xE0101" uv="0x1F000" name="BaseChar5_VS18" />
+ <map uvs="0xE0102" uv="0x1F000" name="None" />
+
+ <map uvs="0xFE01" uv="0x1F001" name="BaseChar6_VS2" />
+ <map uvs="0xE0101" uv="0x1F001" name="BaseChar6_VS18" />
+ <map uvs="0xE0102" uv="0x1F001" name="BaseChar6_VS19" />
+ <map uvs="0xE0103" uv="0x1F001" name="None" />
+
+ <map uvs="0xFE02" uv="0x1F003" name="BaseChar8_VS3" />
+ <map uvs="0xE0102" uv="0x1F003" name="BaseChar8_VS19" />
+ <map uvs="0xE0103" uv="0x1F003" name="BaseChar8_VS20" />
+ <!-- There is no default glyph for U+1F003 U+E0104 but there is a entry for
+ default UVS entry. hasGlyph should return false in this
+ case. -->
+ <map uvs="0xE0104" uv="0x1F003" name="None" />
+ </cmap_format_14>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="BaseChar1" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar1_VS1" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar1_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar1_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar2" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar2_VS2" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar2_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar2_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar3" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar4_VS3" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar4_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar4_VS20" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar5" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar5_VS1" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar5_VS17" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar5_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar6" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar6_VS2" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar6_VS18" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar6_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar7" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar8_VS3" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar8_VS19" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ <TTGlyph name="BaseChar8_VS20" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour></contour><instructions><assembly></assembly></instructions>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Paint.hasGlyph Test
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Paint.hasGlyph Test
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ hasGlyphTestFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Paint.hasGlyph Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ hasGlyphTestFont Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ hasGlyphTestFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
index 4c9b00a..dcf2c89 100644
--- a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -33,7 +33,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mAppsQueryHelper = new AppsQueryHelperTestable(getContext());
+ mAppsQueryHelper = new AppsQueryHelperTestable();
}
public void testQueryAppsSystemAppsOnly() {
@@ -78,33 +78,20 @@
assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
}
- public void testQueryAppsDefaultIme() {
- // Test query default system IMEs
- List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES,
+ public void testQueryAppsImes() {
+ // Test query system IMEs
+ List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_IMES,
true, UserHandle.of(UserHandle.myUserId()));
assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
- // Test query default IMEs
- apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
+ // Test query IMEs
+ apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_IMES, false,
UserHandle.of(UserHandle.myUserId()));
assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "app4"), apps);
-
- // Test that GET_DEFAULT_IMES cannot be used with a user id different from current process
- try {
- mAppsQueryHelper.queryApps(AppsQueryHelper.GET_DEFAULT_IMES, false,
- UserHandle.of(UserHandle.USER_NULL));
- fail("queryApps must fail if wrong user was passed");
- } catch (IllegalArgumentException e) {
- // OK
- }
}
private class AppsQueryHelperTestable extends AppsQueryHelper {
- public AppsQueryHelperTestable(Context context) {
- super(context);
- }
-
@Override
protected List<ApplicationInfo> getAllApps(int userId) {
final ApplicationInfo ai1 = new ApplicationInfo();
@@ -145,18 +132,19 @@
}
@Override
- protected List<InputMethodInfo> getInputMethodList() {
+ protected List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int userId) {
final ResolveInfo sysApp1 = new ResolveInfo();
sysApp1.serviceInfo = new ServiceInfo();
sysApp1.serviceInfo.packageName = "sys_app1";
sysApp1.serviceInfo.name = "name";
- InputMethodInfo imi1 = new InputMethodInfo(sysApp1, false, null, null, 0, true);
+ sysApp1.serviceInfo.applicationInfo = new ApplicationInfo();
+ sysApp1.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
final ResolveInfo app4 = new ResolveInfo();
app4.serviceInfo = new ServiceInfo();
app4.serviceInfo.packageName = "app4";
app4.serviceInfo.name = "name";
- InputMethodInfo imi2 = new InputMethodInfo(app4, false, null, null, 0, true);
- return Arrays.asList(imi1, imi2);
+ app4.serviceInfo.applicationInfo = new ApplicationInfo();
+ return Arrays.asList(sysApp1, app4);
}
}
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index e97bb33..2a3d463 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -20,6 +20,9 @@
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.Arrays;
+import java.util.HashSet;
+
/**
* PaintTest tests {@link Paint}.
*/
@@ -94,4 +97,63 @@
testCase.mWidthWithHinting, widths);
}
}
+
+ private static class HasGlyphTestCase {
+ public final int mBaseCodepoint;
+ public final HashSet<Integer> mVariationSelectors;
+
+ public HasGlyphTestCase(int baseCodepoint, Integer[] variationSelectors) {
+ mBaseCodepoint = baseCodepoint;
+ mVariationSelectors = new HashSet<>(Arrays.asList(variationSelectors));
+ }
+ }
+
+ private static String codePointsToString(int[] codepoints) {
+ StringBuilder sb = new StringBuilder();
+ for (int codepoint : codepoints) {
+ sb.append(Character.toChars(codepoint));
+ }
+ return sb.toString();
+ }
+
+ public void testHasGlyph_variationSelectors() {
+ final Typeface fontTypeface = Typeface.createFromAsset(
+ getInstrumentation().getContext().getAssets(), "fonts/hasGlyphTestFont.ttf");
+ Paint p = new Paint();
+ p.setTypeface(fontTypeface);
+
+ // Usually latin letters U+0061..U+0064 and Mahjong Tiles U+1F000..U+1F003 don't have
+ // variation selectors. This test may fail if system pre-installed fonts have a variation
+ // selector support for U+0061..U+0064 and U+1F000..U+1F003.
+ HasGlyphTestCase[] HAS_GLYPH_TEST_CASES = {
+ new HasGlyphTestCase(0x0061, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}),
+ new HasGlyphTestCase(0x0062, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}),
+ new HasGlyphTestCase(0x0063, new Integer[] {}),
+ new HasGlyphTestCase(0x0064, new Integer[] {0xFE02, 0xE0102, 0xE0103}),
+
+ new HasGlyphTestCase(0x1F000, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}),
+ new HasGlyphTestCase(0x1F001, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}),
+ new HasGlyphTestCase(0x1F002, new Integer[] {}),
+ new HasGlyphTestCase(0x1F003, new Integer[] {0xFE02, 0xE0102, 0xE0103}),
+ };
+
+ for (HasGlyphTestCase testCase : HAS_GLYPH_TEST_CASES) {
+ for (int vs = 0xFE00; vs <= 0xE01EF; ++vs) {
+ // Move to variation selector supplements after variation selectors.
+ if (vs == 0xFF00) {
+ vs = 0xE0100;
+ }
+ final String signature =
+ "hasGlyph(U+" + Integer.toHexString(testCase.mBaseCodepoint) +
+ " U+" + Integer.toHexString(vs) + ")";
+ final String testString =
+ codePointsToString(new int[] {testCase.mBaseCodepoint, vs});
+ if (testCase.mVariationSelectors.contains(vs)) {
+ assertTrue(signature + " is expected to be true", p.hasGlyph(testString));
+ } else {
+ assertFalse(signature + " is expected to be false", p.hasGlyph(testString));
+ }
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 87b3785..3d8fe69 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -17,6 +17,7 @@
package android.widget;
import android.app.Activity;
+import android.content.res.TypedArray;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.Selection;
@@ -25,6 +26,7 @@
import android.text.TextPaint;
import android.text.style.SuggestionSpan;
import android.text.style.TextAppearanceSpan;
+import android.view.View;
import com.android.frameworks.coretests.R;
@@ -54,18 +56,24 @@
final int multiWordSpanStart = 0;
final int multiWordSpanEnd = 11;
- TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity,
- android.R.style.TextAppearance_SuggestionHighlight);
+ TypedArray array = activity.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int id = array.getResourceId(
+ com.android.internal.R.styleable.Theme_textEditSuggestionHighlightStyle, 0);
+ array.recycle();
+
+ TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity, id);
TextPaint tmpTp = new TextPaint();
expectedSpan.updateDrawState(tmpTp);
final int expectedHighlightTextColor = tmpTp.getColor();
final float expectedHighlightTextSize = tmpTp.getTextSize();
- // Create and wait until SuggestionsPopupWindow is shown.
final EditText editText = (EditText) activity.findViewById(R.id.textview);
final Editor editor = editText.getEditorForTesting();
assertNotNull(editor);
- activity.runOnUiThread(new Runnable() {
+
+ // Request to show SuggestionsPopupWindow.
+ Runnable showSuggestionWindowRunner = new Runnable() {
+ @Override
public void run() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(sampleText);
@@ -78,8 +86,7 @@
Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
editText.onTextContextMenuItem(TextView.ID_REPLACE);
}
- });
- getInstrumentation().waitForIdleSync();
+ };
// In this test, the SuggestionsPopupWindow looks like
// abc def ghi
@@ -88,22 +95,26 @@
// | abc *Def* ghi |
// | *ABC DEF GHI* |
// | *Abc Def Ghi* |
+ // -----------------
// | DELETE |
// -----------------
// *XX* means that XX is highlighted.
- activity.runOnUiThread(new Runnable() {
+ Runnable popupVaridator = new Runnable() {
+ @Override
public void run() {
Editor.SuggestionsPopupWindow popupWindow =
editor.getSuggestionsPopupWindowForTesting();
assertNotNull(popupWindow);
- ListView listView = (ListView) popupWindow.getContentViewForTesting();
+ LinearLayout linearLayout = (LinearLayout) popupWindow.getContentViewForTesting();
+ assertNotNull(linearLayout);
+
+ ListView listView = (ListView)linearLayout.findViewById(
+ com.android.internal.R.id.suggestionContainer);
assertNotNull(listView);
int childNum = listView.getChildCount();
- // +1 for "DELETE" command.
- assertEquals(singleWordCandidates.length + multiWordCandidates.length + 1,
- childNum);
+ assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
for (int i = 0; i < singleWordCandidates.length; ++i) {
TextView textView = (TextView) listView.getChildAt(i);
@@ -154,7 +165,30 @@
assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
}
+
+ TextView deleteButton = (TextView)linearLayout.findViewById(
+ com.android.internal.R.id.deleteButton);
+ assertEquals(View.VISIBLE, deleteButton.getWindowVisibility());
+ }
+ };
+
+ // Show the SuggestionWindow and verify the contents.
+ activity.runOnUiThread(showSuggestionWindowRunner);
+ getInstrumentation().waitForIdleSync();
+ activity.runOnUiThread(popupVaridator);
+
+ // Request to hide the SuggestionPopupWindow and wait until it is hidden.
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText("");
}
});
+ getInstrumentation().waitForIdleSync();
+
+ // Show and verify the contents again.
+ activity.runOnUiThread(showSuggestionWindowRunner);
+ getInstrumentation().waitForIdleSync();
+ activity.runOnUiThread(popupVaridator);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
new file mode 100644
index 0000000..951e87a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import junit.framework.TestCase;
+
+public final class HexDumpTest extends TestCase {
+ public void testBytesToHexString() {
+ assertEquals("abcdef", HexDump.toHexString(
+ new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, false));
+ assertEquals("ABCDEF", HexDump.toHexString(
+ new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 27c6620..ab37519 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -143,7 +143,9 @@
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save-except-idle package="com.android.providers.downloads" />
+ <!-- These are the packages that are white-listed to be able to run as system user -->
+ <system-user-whitelisted-app package="com.android.settings" />
+
<!-- These are the packages that shouldn't run as system user -->
<system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
- <system-user-blacklisted-app package="com.android.settings" />
</permissions>
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
index 58bf654..4186007 100644
--- a/data/keyboards/qwerty.kl
+++ b/data/keyboards/qwerty.kl
@@ -81,7 +81,7 @@
key 39 SEMICOLON
key 40 APOSTROPHE
key 14 DEL
-
+
key 44 Z
key 45 X
key 46 C
@@ -93,7 +93,7 @@
key 52 PERIOD
key 53 SLASH
key 28 ENTER
-
+
key 56 ALT_LEFT
key 100 ALT_RIGHT
key 42 SHIFT_LEFT
@@ -101,7 +101,7 @@
key 15 TAB
key 57 SPACE
key 150 EXPLORER
-key 155 ENVELOPE
+key 155 ENVELOPE
key 12 MINUS
key 13 EQUALS
@@ -110,3 +110,16 @@
# On an AT keyboard: ESC, F10
key 1 BACK
key 68 MENU
+
+# App switch = Overview key
+key 580 APP_SWITCH
+
+# Media control keys
+key 160 MEDIA_CLOSE
+key 161 MEDIA_EJECT
+key 163 MEDIA_NEXT
+key 164 MEDIA_PLAY_PAUSE
+key 165 MEDIA_PREVIOUS
+key 166 MEDIA_STOP
+key 167 MEDIA_RECORD
+key 168 MEDIA_REWIND
diff --git a/docs/html/guide/components/services.jd b/docs/html/guide/components/services.jd
index 6e22be8..b8c105d 100644
--- a/docs/html/guide/components/services.jd
+++ b/docs/html/guide/components/services.jd
@@ -512,7 +512,7 @@
onStartCommand()} directly.)</p>
<p>For example, an activity can start the example service in the previous section ({@code
-HelloSevice}) using an explicit intent with {@link android.content.Context#startService
+HelloService}) using an explicit intent with {@link android.content.Context#startService
startService()}:</p>
<pre>
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index c1ccef0..21e3057 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -562,10 +562,11 @@
<tr>
<td rowspan="6">Camera</td>
<td><code>android.hardware.camera</code></td>
- <td>The application uses the device's camera. If the device supports
- multiple cameras, the application uses the camera that facing
- away from the screen.</td>
- <td></td>
+ <td>The application uses the device's back-facing (main) camera.</td>
+ <td>Devices with only a front-facing camera do not list this feature, so the
+ <code>android.hardware.camera.any</code> feature should be
+ used instead if a camera facing any direction is acceptable for the
+ application.</td>
</tr>
<tr>
<td><code>android.hardware.camera.autofocus</code></td>
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 39d13df..0ee877e 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -913,16 +913,26 @@
protected void onBoundsChange(Rect bounds) {}
/**
- * Return the intrinsic width of the underlying drawable object. Returns
- * -1 if it has no intrinsic width, such as with a solid color.
+ * Returns the drawable's intrinsic width.
+ * <p>
+ * Intrinsic width is the width at which the drawable would like to be laid
+ * out, including any inherent padding. If the drawable has no intrinsic
+ * width, such as a solid color, this method returns -1.
+ *
+ * @return the intrinsic width, or -1 if no intrinsic width
*/
public int getIntrinsicWidth() {
return -1;
}
/**
- * Return the intrinsic height of the underlying drawable object. Returns
- * -1 if it has no intrinsic height, such as with a solid color.
+ * Returns the drawable's intrinsic height.
+ * <p>
+ * Intrinsic height is the height at which the drawable would like to be
+ * laid out, including any inherent padding. If the drawable has no
+ * intrinsic height, such as a solid color, this method returns -1.
+ *
+ * @return the intrinsic height, or -1 if no intrinsic height
*/
public int getIntrinsicHeight() {
return -1;
@@ -1230,30 +1240,54 @@
*/
public static abstract class ConstantState {
/**
- * Create a new drawable without supplying resources the caller
- * is running in. Note that using this means the density-dependent
- * drawables (like bitmaps) will not be able to update their target
- * density correctly. One should use {@link #newDrawable(Resources)}
- * instead to provide a resource.
+ * Creates a new Drawable instance from its constant state.
+ * <p>
+ * <strong>Note:</strong> Using this method means density-dependent
+ * properties, such as pixel dimensions or bitmap images, will not be
+ * updated to match the density of the target display. To ensure
+ * correct scaling, use {@link #newDrawable(Resources)} instead to
+ * provide an appropriate Resources object.
+ *
+ * @return a new drawable object based on this constant state
+ * @see {@link #newDrawable(Resources)}
*/
+ @NonNull
public abstract Drawable newDrawable();
/**
- * Create a new Drawable instance from its constant state. This
- * must be implemented for drawables that change based on the target
- * density of their caller (that is depending on whether it is
- * in compatibility mode).
+ * Creates a new Drawable instance from its constant state using the
+ * specified resources. This method should be implemented for drawables
+ * that have density-dependent properties.
+ * <p>
+ * The default implementation for this method calls through to
+ * {@link #newDrawable()}.
+ *
+ * @param res the resources of the context in which the drawable will
+ * be displayed
+ * @return a new drawable object based on this constant state
*/
- public Drawable newDrawable(Resources res) {
+ @NonNull
+ public Drawable newDrawable(@Nullable Resources res) {
return newDrawable();
}
/**
- * Create a new Drawable instance from its constant state. This must be
- * implemented for drawables that can have a theme applied.
+ * Creates a new Drawable instance from its constant state using the
+ * specified resources and theme. This method should be implemented for
+ * drawables that have theme-dependent properties.
+ * <p>
+ * The default implementation for this method calls through to
+ * {@link #newDrawable(Resources)}.
+ *
+ * @param res the resources of the context in which the drawable will
+ * be displayed
+ * @param theme the theme of the context in which the drawable will be
+ * displayed
+ * @return a new drawable object based on this constant state
*/
- public Drawable newDrawable(Resources res, Theme theme) {
- return newDrawable(null);
+ @NonNull
+ public Drawable newDrawable(@Nullable Resources res, @Nullable Theme theme) {
+ return newDrawable(res);
}
/**
@@ -1390,7 +1424,7 @@
}
}
- static int resolveDensity(@NonNull Resources r, int parentDensity) {
+ static int resolveDensity(@Nullable Resources r, int parentDensity) {
final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 927b9c9..36d4272 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -222,12 +222,20 @@
@Override
public int getIntrinsicWidth() {
- return getDrawable().getIntrinsicWidth() + mState.mInsetLeft + mState.mInsetRight;
+ final int childWidth = getDrawable().getIntrinsicWidth();
+ if (childWidth < 0) {
+ return -1;
+ }
+ return childWidth + mState.mInsetLeft + mState.mInsetRight;
}
@Override
public int getIntrinsicHeight() {
- return getDrawable().getIntrinsicHeight() + mState.mInsetTop + mState.mInsetBottom;
+ final int childHeight = getDrawable().getIntrinsicHeight();
+ if (childHeight < 0) {
+ return -1;
+ }
+ return childHeight + mState.mInsetTop + mState.mInsetBottom;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 2d378c6..e83513c 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -20,6 +20,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.util.DisplayMetrics;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
@@ -50,7 +51,7 @@
protected float mTargetRadius;
/** Screen density used to adjust pixel-based constants. */
- protected float mDensity;
+ protected float mDensityScale;
/**
* If set, force all ripple animations to not run on RenderThread, even if it would be
@@ -71,7 +72,7 @@
}
}
- public final void setup(float maxRadius, float density) {
+ public final void setup(float maxRadius, int densityDpi) {
if (maxRadius >= 0) {
mHasMaxRadius = true;
mTargetRadius = maxRadius;
@@ -79,7 +80,7 @@
mTargetRadius = getTargetRadius(mBounds);
}
- mDensity = density;
+ mDensityScale = densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
onTargetRadiusChanged(mTargetRadius);
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 52e7f24..aaab529 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -160,7 +160,7 @@
private Paint mRipplePaint;
/** Target density of the display into which ripples are drawn. */
- private float mDensity = 1.0f;
+ private int mDensity;
/** Whether bounds are being overridden. */
private boolean mOverrideBounds;
@@ -544,8 +544,7 @@
mBackground = new RippleBackground(this, mHotspotBounds, mForceSoftware);
}
- final float densityScale = mState.mDensity * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- mBackground.setup(mState.mMaxRadius, densityScale);
+ mBackground.setup(mState.mMaxRadius, mDensity);
mBackground.enter(focused);
}
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index c660846..829733e 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -168,7 +168,7 @@
}
final int duration = (int)
- (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
+ (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
tweenRadius.setAutoCancel(true);
@@ -204,7 +204,7 @@
private int getRadiusExitDuration() {
final float remainingRadius = mTargetRadius - getCurrentRadius();
return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
- + WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
+ + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
}
private float getCurrentRadius() {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index eee9b24..f630055e 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -231,6 +231,9 @@
private int mDpiScaledHeight = 0;
private Insets mDpiScaledInsets = Insets.NONE;
+ /** Whether DPI-scaled width, height, and insets need to be updated. */
+ private boolean mDpiScaledDirty = true;
+
// Temp variable, only for saving "new" operation at the draw() time.
private final float[] mTmpFloats = new float[9];
private final Matrix mTmpMatrix = new Matrix();
@@ -259,9 +262,13 @@
* displayed, or {@code null} to use the constant state defaults
*/
private void updateLocalState(Resources res) {
- mTargetDensity = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mSourceDensity);
+ final int density = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mDensity);
+ if (mTargetDensity != density) {
+ mTargetDensity = density;
+ mDpiScaledDirty = true;
+ }
+
mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
- computeVectorSize();
}
@Override
@@ -422,17 +429,26 @@
@Override
public int getIntrinsicWidth() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledWidth;
}
@Override
public int getIntrinsicHeight() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledHeight;
}
/** @hide */
@Override
public Insets getOpticalInsets() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledInsets;
}
@@ -444,7 +460,7 @@
final VPathRenderer pathRenderer = mVectorState.mVPathRenderer;
final Insets opticalInsets = pathRenderer.mOpticalInsets;
- final int sourceDensity = pathRenderer.mSourceDensity;
+ final int sourceDensity = pathRenderer.mDensity;
final int targetDensity = mTargetDensity;
if (targetDensity != sourceDensity) {
mDpiScaledWidth = Drawable.scaleFromDensity(
@@ -465,6 +481,8 @@
mDpiScaledHeight = (int) pathRenderer.mBaseHeight;
mDpiScaledInsets = opticalInsets;
}
+
+ mDpiScaledDirty = false;
}
@Override
@@ -481,6 +499,11 @@
return;
}
+ final VPathRenderer path = state.mVPathRenderer;
+ final boolean changedDensity = path.setDensity(
+ Drawable.resolveDensity(t.getResources(), 0));
+ mDpiScaledDirty |= changedDensity;
+
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.VectorDrawable);
@@ -492,6 +515,9 @@
} finally {
a.recycle();
}
+
+ // May have changed size.
+ mDpiScaledDirty = true;
}
// Apply theme to contained color state list.
@@ -499,7 +525,6 @@
state.mTint = state.mTint.obtainForTheme(t);
}
- final VPathRenderer path = state.mVPathRenderer;
if (path != null && path.canApplyTheme()) {
path.applyTheme(t);
}
@@ -565,21 +590,24 @@
}
@Override
- public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
- final VPathRenderer pathRenderer = new VPathRenderer();
- state.mVPathRenderer = pathRenderer;
+ state.mVPathRenderer = new VPathRenderer();
+ state.mVPathRenderer.setDensity(Drawable.resolveDensity(r, 0));
- final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawable);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
updateStateFromTypedArray(a);
a.recycle();
+ mDpiScaledDirty = true;
+
state.mCacheDirty = true;
- inflateInternal(res, parser, attrs, theme);
+ inflateChildElements(r, parser, attrs, theme);
// Update local properties.
- updateLocalState(res);
+ updateLocalState(r);
}
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -592,13 +620,6 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- // The density may have changed since the last update (if any). Any
- // dimension-type attributes will need their default values scaled.
- final int targetDensity = Drawable.resolveDensity(a.getResources(), 0);
- final int sourceDensity = pathRenderer.mSourceDensity;
- final float densityScale = targetDensity / (float) sourceDensity;
- pathRenderer.mSourceDensity = targetDensity;
-
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
if (tintMode != -1) {
state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
@@ -626,11 +647,9 @@
}
pathRenderer.mBaseWidth = a.getDimension(
- R.styleable.VectorDrawable_width,
- pathRenderer.mBaseWidth * densityScale);
+ R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
pathRenderer.mBaseHeight = a.getDimension(
- R.styleable.VectorDrawable_height,
- pathRenderer.mBaseHeight * densityScale);
+ R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
if (pathRenderer.mBaseWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
@@ -641,21 +660,17 @@
}
final int insetLeft = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetLeft,
- (int) (pathRenderer.mOpticalInsets.left * densityScale));
+ R.styleable.VectorDrawable_opticalInsetLeft, pathRenderer.mOpticalInsets.left);
final int insetTop = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetTop,
- (int) (pathRenderer.mOpticalInsets.top * densityScale));
+ R.styleable.VectorDrawable_opticalInsetTop, pathRenderer.mOpticalInsets.top);
final int insetRight = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetRight,
- (int) (pathRenderer.mOpticalInsets.right * densityScale));
+ R.styleable.VectorDrawable_opticalInsetRight, pathRenderer.mOpticalInsets.right);
final int insetBottom = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetBottom,
- (int) (pathRenderer.mOpticalInsets.bottom * densityScale));
+ R.styleable.VectorDrawable_opticalInsetBottom, pathRenderer.mOpticalInsets.bottom);
pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
- final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
- pathRenderer.getAlpha());
+ final float alphaInFloat = a.getFloat(
+ R.styleable.VectorDrawable_alpha, pathRenderer.getAlpha());
pathRenderer.setAlpha(alphaInFloat);
final String name = a.getString(R.styleable.VectorDrawable_name);
@@ -665,7 +680,7 @@
}
}
- private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
+ private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = state.mVPathRenderer;
@@ -929,7 +944,7 @@
int mRootAlpha = 0xFF;
String mRootName = null;
- int mSourceDensity = DisplayMetrics.DENSITY_DEFAULT;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -966,12 +981,37 @@
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
- mSourceDensity = copy.mSourceDensity;
+ mDensity = copy.mDensity;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
}
+ public final boolean setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+ applyDensityScaling(sourceDensity, targetDensity);
+ return true;
+ }
+ return false;
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity);
+ mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity);
+
+ final int insetLeft = Drawable.scaleFromDensity(
+ mOpticalInsets.left, sourceDensity, targetDensity, false);
+ final int insetTop = Drawable.scaleFromDensity(
+ mOpticalInsets.top, sourceDensity, targetDensity, false);
+ final int insetRight = Drawable.scaleFromDensity(
+ mOpticalInsets.right, sourceDensity, targetDensity, false);
+ final int insetBottom = Drawable.scaleFromDensity(
+ mOpticalInsets.bottom, sourceDensity, targetDensity, false);
+ mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
+ }
+
public boolean canApplyTheme() {
return mRootGroup.canApplyTheme();
}
@@ -1321,7 +1361,7 @@
* Common Path information for clip path and normal path.
*/
private static abstract class VPath implements VObject {
- protected PathParser.PathDataNode[] mNodes = null;
+ protected PathParser.PathData mPathData = null;
String mPathName;
int mChangingConfigurations;
@@ -1332,7 +1372,7 @@
public VPath(VPath copy) {
mPathName = copy.mPathName;
mChangingConfigurations = copy.mChangingConfigurations;
- mNodes = PathParser.deepCopyNodes(copy.mNodes);
+ mPathData = copy.mPathData == null ? null : new PathParser.PathData(copy.mPathData);
}
public String getPathName() {
@@ -1345,18 +1385,14 @@
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
- public PathParser.PathDataNode[] getPathData() {
- return mNodes;
+ public PathParser.PathData getPathData() {
+ return mPathData;
}
+ // TODO: Move the PathEvaluator and this setter and the getter above into native.
@SuppressWarnings("unused")
- public void setPathData(PathParser.PathDataNode[] nodes) {
- if (!PathParser.canMorph(mNodes, nodes)) {
- // This should not happen in the middle of animation.
- mNodes = PathParser.deepCopyNodes(nodes);
- } else {
- PathParser.updateNodes(mNodes, nodes);
- }
+ public void setPathData(PathParser.PathData pathData) {
+ mPathData.setPathData(pathData);
}
@Override
@@ -1392,8 +1428,8 @@
* @param outPath the output path
*/
protected void toPath(TempState temp, Path outPath) {
- if (mNodes != null) {
- PathParser.PathDataNode.nodesToPath(mNodes, outPath);
+ if (mPathData != null) {
+ PathParser.createPathFromPathData(outPath, mPathData);
}
}
@@ -1488,9 +1524,9 @@
mPathName = pathName;
}
- final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData);
- if (pathData != null) {
- mNodes = PathParser.createNodesFromPathData(pathData);
+ final String pathDataString = a.getString(R.styleable.VectorDrawableClipPath_pathData);
+ if (pathDataString != null) {
+ mPathData = new PathParser.PathData(pathDataString);
}
}
@@ -1719,9 +1755,9 @@
mPathName = pathName;
}
- final String pathData = a.getString(R.styleable.VectorDrawablePath_pathData);
- if (pathData != null) {
- mNodes = PathParser.createNodesFromPathData(pathData);
+ final String pathString = a.getString(R.styleable.VectorDrawablePath_pathData);
+ if (pathString != null) {
+ mPathData = new PathParser.PathData(pathString);
}
final ColorStateList fillColors = a.getColorStateList(
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index c31a8b7..8c20ddc 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -101,20 +101,21 @@
*/
public static void install() {
Provider[] providers = Security.getProviders();
- int bcProviderPosition = -1;
- for (int position = 0; position < providers.length; position++) {
- Provider provider = providers[position];
+ int bcProviderIndex = -1;
+ for (int i = 0; i < providers.length; i++) {
+ Provider provider = providers[i];
if ("BC".equals(provider.getName())) {
- bcProviderPosition = position;
+ bcProviderIndex = i;
break;
}
}
Security.addProvider(new AndroidKeyStoreProvider());
Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
- if (bcProviderPosition != -1) {
+ if (bcProviderIndex != -1) {
// Bouncy Castle provider found -- install the workaround provider above it.
- Security.insertProviderAt(workaroundProvider, bcProviderPosition);
+ // insertProviderAt uses 1-based positions.
+ Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
} else {
// Bouncy Castle provider not found -- install the workaround provider at lowest
// priority.
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 5cdd723..cc68fb2 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@
font/Font.cpp \
renderstate/Blend.cpp \
renderstate/MeshState.cpp \
+ renderstate/OffscreenBufferPool.cpp \
renderstate/PixelBufferState.cpp \
renderstate/RenderState.cpp \
renderstate/Scissor.cpp \
@@ -28,12 +29,15 @@
utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
utils/TestWindowContext.cpp \
+ utils/VectorDrawableUtils.cpp \
+ utils/TestUtils.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
Animator.cpp \
AnimatorManager.cpp \
AssetAtlas.cpp \
Caches.cpp \
+ Canvas.cpp \
CanvasState.cpp \
ClipArea.cpp \
DamageAccumulator.cpp \
@@ -205,7 +209,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
-LOCAL_CFLAGS := $(hwui_cflags)
+LOCAL_CFLAGS := \
+ $(hwui_cflags) \
+ -DHWUI_NULL_GPU
LOCAL_SRC_FILES += \
unit_tests/CanvasStateTests.cpp \
@@ -215,7 +221,8 @@
unit_tests/FatVectorTests.cpp \
unit_tests/LayerUpdateQueueTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
- unit_tests/PathParserTests.cpp \
+ unit_tests/VectorDrawableTests.cpp \
+ unit_tests/OffscreenBufferPoolTests.cpp \
unit_tests/StringUtilsTests.cpp
ifeq (true, $(HWUI_NEW_OPS))
@@ -248,9 +255,11 @@
LOCAL_SRC_FILES += \
tests/TestContext.cpp \
- tests/TreeContentAnimation.cpp \
+ tests/TestSceneRunner.cpp \
tests/main.cpp
+LOCAL_SRC_FILES += $(call all-cpp-files-under, tests/scenes)
+
include $(BUILD_EXECUTABLE)
# ------------------------
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 2fca5ea..d13d7ef 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -19,104 +19,32 @@
#include "Caches.h"
#include "Glop.h"
#include "GlopBuilder.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
#include "utils/GLUtils.h"
+#include "VertexBuffer.h"
+
+#include <algorithm>
+#include <math.h>
namespace android {
namespace uirenderer {
////////////////////////////////////////////////////////////////////////////////
-// OffscreenBuffer
-////////////////////////////////////////////////////////////////////////////////
-
-OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t textureWidth, uint32_t textureHeight,
- uint32_t viewportWidth, uint32_t viewportHeight)
- : renderState(renderState)
- , viewportWidth(viewportWidth)
- , viewportHeight(viewportHeight)
- , texture(caches) {
- texture.width = textureWidth;
- texture.height = textureHeight;
-
- caches.textureState().activateTexture(0);
- glGenTextures(1, &texture.id);
- caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
-
- texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
- // not setting filter on texture, since it's set when drawing, based on transform
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-}
-
-void OffscreenBuffer::updateMeshFromRegion() {
- // avoid T-junctions as they cause artifacts in between the resultant
- // geometry when complex transforms occur.
- // TODO: generate the safeRegion only if necessary based on drawing transform
- Region safeRegion = Region::createTJunctionFreeRegion(region);
-
- size_t count;
- const android::Rect* rects = safeRegion.getArray(&count);
-
- const float texX = 1.0f / float(viewportWidth);
- const float texY = 1.0f / float(viewportHeight);
-
- FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
- TextureVertex* mesh = &meshVector[0];
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (viewportHeight - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (viewportHeight - r->bottom) * texY;
-
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- }
- elementCount = count * 6;
- renderState.meshState().genOrUpdateMeshBuffer(&vbo,
- sizeof(TextureVertex) * count * 4,
- &meshVector[0],
- GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
-}
-
-OffscreenBuffer::~OffscreenBuffer() {
- texture.deleteTexture();
- renderState.meshState().deleteMeshBuffer(vbo);
- elementCount = 0;
- vbo = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
- uint32_t width, uint32_t height) {
- // TODO: get from cache!
- return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
-}
-
-void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
- // TODO: return texture/offscreenbuffer to cache!
- delete offscreenBuffer;
-}
-
-OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
- OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
- startLayer(buffer);
+ OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
+ startRepaintLayer(buffer, Rect(width, height));
return buffer;
}
-void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
+void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
+ LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
+
mRenderTarget.offscreenBuffer = offscreenBuffer;
// create and bind framebuffer
@@ -130,12 +58,10 @@
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
- // Clear the FBO
- mRenderState.scissor().setEnabled(false);
- glClear(GL_COLOR_BUFFER_BIT);
-
// Change the viewport & ortho projection
setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
+
+ clearColorBuffer(repaintRect);
}
void BakedOpRenderer::endLayer() {
@@ -149,16 +75,13 @@
mRenderTarget.frameBufferId = -1;
}
-void BakedOpRenderer::startFrame(uint32_t width, uint32_t height) {
+void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
mRenderState.bindFramebuffer(0);
setViewport(width, height);
mCaches.clearGarbage();
if (!mOpaque) {
- // TODO: partial invalidate!
- mRenderState.scissor().setEnabled(false);
- glClear(GL_COLOR_BUFFER_BIT);
- mHasDrawn = true;
+ clearColorBuffer(repaintRect);
}
}
@@ -188,6 +111,20 @@
mRenderState.blend().syncEnabled();
}
+void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
+ if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+ // Full viewport is being cleared - disable scissor
+ mRenderState.scissor().setEnabled(false);
+ } else {
+ // Requested rect is subset of viewport - scissor to it to avoid over-clearing
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(rect.left, mRenderTarget.viewportHeight - rect.bottom,
+ rect.getWidth(), rect.getHeight());
+ }
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (!mRenderTarget.frameBufferId) mHasDrawn = true;
+}
+
Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
if (!texture) {
@@ -211,7 +148,7 @@
mRenderTarget.offscreenBuffer->region.orSelf(dirty);
}
mRenderState.render(glop, mRenderTarget.orthoMatrix);
- mHasDrawn = true;
+ if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
////////////////////////////////////////////////////////////////////////////////
@@ -249,6 +186,10 @@
renderer.renderGlop(state, glop);
}
+void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("todo");
+}
+
void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -261,6 +202,69 @@
renderer.renderGlop(state, glop);
}
+namespace VertexBufferRenderFlags {
+ enum {
+ Offset = 0x1,
+ ShadowInterp = 0x2,
+ };
+}
+
+static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
+ const VertexBuffer& vertexBuffer, float translateX, float translateY,
+ SkPaint& paint, int vertexBufferRenderFlags) {
+ if (CC_LIKELY(vertexBuffer.getVertexCount())) {
+ bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
+ const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+ .setFillPaint(paint, state.alpha)
+ .setTransform(state.computedState.transform, transformFlags)
+ .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+ .build();
+ renderer.renderGlop(state, glop);
+ }
+}
+
+static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
+ SkPaint paint;
+ paint.setAntiAlias(true); // want to use AlphaVertex
+
+ // The caller has made sure casterAlpha > 0.
+ uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
+ if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
+ ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
+ }
+ if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
+ paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
+ renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
+ paint, VertexBufferRenderFlags::ShadowInterp);
+ }
+
+ uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
+ if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
+ spotShadowAlpha = Properties::overrideSpotShadowStrength;
+ }
+ if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
+ paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
+ renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
+ paint, VertexBufferRenderFlags::ShadowInterp);
+ }
+}
+
+void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
+ TessellationCache::vertexBuffer_pair_t buffers;
+ renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
+ op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
+ &op.shadowMatrixXY, &op.shadowMatrixZ,
+ op.lightCenter, renderer.getLightInfo().lightRadius,
+ buffers);
+
+ renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
+}
+
void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -273,6 +277,91 @@
renderer.renderGlop(state, glop);
}
+static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
+ const TextOp& op, const BakedOpState& state) {
+ renderer.caches().textureState().activateTexture(0);
+
+ PaintUtils::TextShadow textShadow;
+ if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
+ LOG_ALWAYS_FATAL("failed to query shadow attributes");
+ }
+
+ renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
+ ShadowTexture* texture = renderer.caches().dropShadowCache.get(
+ op.paint, (const char*) op.glyphs,
+ op.glyphCount, textShadow.radius, op.positions);
+ // If the drop shadow exceeds the max texture size or couldn't be
+ // allocated, skip drawing
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ const float sx = op.x - texture->left + textShadow.dx;
+ const float sy = op.y - texture->top + textShadow.dy;
+
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUnitQuad(nullptr)
+ .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
+ FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
+
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
+ fontRenderer.setFont(op.paint, SkMatrix::I());
+ renderTextShadow(renderer, fontRenderer, op, state);
+ }
+
+ float x = op.x;
+ float y = op.y;
+ const Matrix4& transform = state.computedState.transform;
+ const bool pureTranslate = transform.isPureTranslate();
+ if (CC_LIKELY(pureTranslate)) {
+ x = floorf(x + transform.getTranslateX() + 0.5f);
+ y = floorf(y + transform.getTranslateY() + 0.5f);
+ fontRenderer.setFont(op.paint, SkMatrix::I());
+ fontRenderer.setTextureFiltering(false);
+ } else if (CC_UNLIKELY(transform.isPerspective())) {
+ fontRenderer.setFont(op.paint, SkMatrix::I());
+ fontRenderer.setTextureFiltering(true);
+ } else {
+ // We only pass a partial transform to the font renderer. That partial
+ // matrix defines how glyphs are rasterized. Typically we want glyphs
+ // to be rasterized at their final size on screen, which means the partial
+ // matrix needs to take the scale factor into account.
+ // When a partial matrix is used to transform glyphs during rasterization,
+ // the mesh is generated with the inverse transform (in the case of scale,
+ // the mesh is generated at 1.0 / scale for instance.) This allows us to
+ // apply the full transform matrix at draw time in the vertex shader.
+ // Applying the full matrix in the shader is the easiest way to handle
+ // rotation and perspective and allows us to always generated quads in the
+ // font renderer which greatly simplifies the code, clipping in particular.
+ float sx, sy;
+ transform.decomposeScale(sx, sy);
+ fontRenderer.setFont(op.paint, SkMatrix::MakeScale(
+ roundf(std::max(1.0f, sx)),
+ roundf(std::max(1.0f, sy))));
+ fontRenderer.setTextureFiltering(true);
+ }
+
+ // TODO: Implement better clipping for scaled/rotated text
+ const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect;
+ Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+
+ int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+ TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint);
+
+ bool hasActiveLayer = false; // TODO
+ fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y,
+ op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging
+}
+
void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
OffscreenBuffer* buffer = *op.layerHandle;
@@ -291,7 +380,7 @@
renderer.renderGlop(state, glop);
if (op.destroy) {
- BakedOpRenderer::destroyOffscreenBuffer(buffer);
+ renderer.renderState().layerPool().putOrDelete(buffer);
}
}
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index aa1e67d..29f9a6f 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -29,33 +29,6 @@
class RenderState;
/**
- * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
- * encompasses enough information to draw it back on screen (minus paint properties, which are held
- * by LayerOp).
- */
-class OffscreenBuffer {
-public:
- OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t textureWidth, uint32_t textureHeight,
- uint32_t viewportWidth, uint32_t viewportHeight);
- ~OffscreenBuffer();
-
- // must be called prior to rendering, to construct/update vertex buffer
- void updateMeshFromRegion();
-
- RenderState& renderState;
- uint32_t viewportWidth;
- uint32_t viewportHeight;
- Texture texture;
-
- // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
- // drawing back to screen / parent FBO.
- Region region;
- GLsizei elementCount = 0;
- GLuint vbo = 0;
-};
-
-/**
* Main rendering manager for a collection of work - one frame + any contained FBOs.
*
* Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
@@ -66,31 +39,39 @@
*/
class BakedOpRenderer {
public:
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque)
+ /**
+ * Position agnostic shadow lighting info. Used with all shadow ops in scene.
+ */
+ struct LightInfo {
+ float lightRadius = 0;
+ uint8_t ambientShadowAlpha = 0;
+ uint8_t spotShadowAlpha = 0;
+ };
+
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo)
: mRenderState(renderState)
, mCaches(caches)
- , mOpaque(opaque) {
+ , mOpaque(opaque)
+ , mLightInfo(lightInfo) {
}
- static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
- uint32_t width, uint32_t height);
- static void destroyOffscreenBuffer(OffscreenBuffer*);
-
RenderState& renderState() { return mRenderState; }
Caches& caches() { return mCaches; }
- void startFrame(uint32_t width, uint32_t height);
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
void endFrame();
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
- void startLayer(OffscreenBuffer* offscreenBuffer);
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);
+ const LightInfo& getLightInfo() { return mLightInfo; }
void renderGlop(const BakedOpState& state, const Glop& glop);
bool didDraw() { return mHasDrawn; }
private:
void setViewport(uint32_t width, uint32_t height);
+ void clearColorBuffer(const Rect& clearRect);
RenderState& mRenderState;
Caches& mCaches;
@@ -106,6 +87,8 @@
uint32_t viewportHeight = 0;
Matrix4 orthoMatrix;
} mRenderTarget;
+
+ const LightInfo mLightInfo;
};
/**
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index ddb8c84..9a40c3b 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -89,6 +89,21 @@
* and early return null in one place.
*/
}
+
+ /**
+ * Constructor for unbounded ops without transform/clip (namely shadows)
+ *
+ * Since the op doesn't have known bounds, we conservatively set the mapped bounds
+ * to the current clipRect, and clipSideFlags to Full.
+ */
+ ResolvedRenderState(const Snapshot& snapshot) {
+ transform = *snapshot.transform;
+ clipRect = snapshot.getRenderTargetClip();
+ clippedBounds = clipRect;
+ transform.mapRect(clippedBounds);
+ clipSideFlags = OpClipSideFlags::Full;
+ }
+
Matrix4 transform;
Rect clipRect;
int clipSideFlags = 0;
@@ -104,8 +119,7 @@
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
const Snapshot& snapshot, const RecordedOp& recordedOp) {
- BakedOpState* bakedOp = new (allocator) BakedOpState(
- snapshot, recordedOp);
+ BakedOpState* bakedOp = new (allocator) BakedOpState(snapshot, recordedOp);
if (bakedOp->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
allocator.rewindIfLastAlloc(bakedOp);
@@ -114,6 +128,14 @@
return bakedOp;
}
+ static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
+ const Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
+ if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
+
+ // clip isn't empty, so construct the op
+ return new (allocator) BakedOpState(snapshot, shadowOpPtr);
+ }
+
static void* operator new(size_t size, LinearAllocator& allocator) {
return allocator.alloc(size);
}
@@ -134,6 +156,13 @@
, roundRectClipState(snapshot.roundRectClipState)
, projectionPathMask(snapshot.projectionPathMask)
, op(&recordedOp) {}
+
+ BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr)
+ : computedState(snapshot)
+ , alpha(snapshot.alpha)
+ , roundRectClipState(snapshot.roundRectClipState)
+ , projectionPathMask(snapshot.projectionPathMask)
+ , op(shadowOpPtr) {}
};
}; // namespace uirenderer
diff --git a/libs/hwui/Canvas.cpp b/libs/hwui/Canvas.cpp
new file mode 100644
index 0000000..bc88c81
--- /dev/null
+++ b/libs/hwui/Canvas.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Canvas.h"
+
+#include <SkDrawFilter.h>
+
+namespace android {
+
+void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+ uint32_t flags;
+ SkDrawFilter* drawFilter = getDrawFilter();
+ if (drawFilter) {
+ SkPaint paintCopy(paint);
+ drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ flags = paintCopy.getFlags();
+ } else {
+ flags = paint.getFlags();
+ }
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ // Same values used by Skia
+ const float kStdStrikeThru_Offset = (-6.0f / 21.0f);
+ const float kStdUnderline_Offset = (1.0f / 9.0f);
+ const float kStdUnderline_Thickness = (1.0f / 18.0f);
+
+ SkScalar left = x;
+ SkScalar right = x + length;
+ float textSize = paint.getTextSize();
+ float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+ drawRect(left, top, right, bottom, paint);
+ }
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+ SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+ drawRect(left, top, right, bottom, paint);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 4bd4ac8..b585a27 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -149,16 +149,12 @@
// Text
/**
* drawText: count is of glyphs
- * totalAdvance is ignored in software renderering, used by hardware renderer for
- * text decorations (underlines, strikethroughs).
+ * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) = 0;
- /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */
- virtual void drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint) = 0;
/** drawTextOnPath: count is of glyphs */
virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) = 0;
@@ -171,6 +167,9 @@
* to be added to each glyph's position to get its absolute position.
*/
virtual bool drawTextAbsolutePos() const = 0;
+
+protected:
+ void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
};
}; // namespace android
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 03b1706..4cfbb2a 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -18,6 +18,7 @@
#include "Extensions.h"
#include <GLES2/gl2.h>
+#include <log/log.h>
#include <thread>
#include <mutex>
@@ -29,6 +30,7 @@
static std::once_flag sInitializedFlag;
const DeviceInfo* DeviceInfo::get() {
+ LOG_ALWAYS_FATAL_IF(!sDeviceInfo, "DeviceInfo not yet initialized.");
return sDeviceInfo;
}
@@ -40,7 +42,6 @@
}
void DeviceInfo::load() {
- mExtensions.load();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
}
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index f5e5735..759c12a 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -423,18 +423,6 @@
addDrawOp(op);
}
-void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions,
- int count, int posCount, const SkPaint& paint) {
- if (!text || count <= 0) return;
-
- int bytesCount = 2 * count;
- positions = refBuffer<float>(positions, count * 2);
-
- DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount),
- bytesCount, count, positions, refPaint(&paint));
- addDrawOp(op);
-}
-
void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
int count, const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
@@ -450,6 +438,7 @@
DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
x, y, positions, refPaint(&paint), totalAdvance, bounds);
addDrawOp(op);
+ drawTextDecorations(x, y, totalAdvance, paint);
}
void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index fc08504..bf98f79 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -17,6 +17,14 @@
#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+#include "Canvas.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "RenderNode.h"
+#include "ResourceCache.h"
+#include "SkiaCanvasProxy.h"
+#include "utils/Macros.h"
+
#include <SkDrawFilter.h>
#include <SkMatrix.h>
#include <SkPaint.h>
@@ -25,13 +33,6 @@
#include <SkTLazy.h>
#include <cutils/compiler.h>
-#include "Canvas.h"
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "SkiaCanvasProxy.h"
-#include "RenderNode.h"
-#include "ResourceCache.h"
-
namespace android {
namespace uirenderer {
@@ -66,7 +67,7 @@
virtual ~DisplayListCanvas();
void reset(int width, int height);
- __attribute__((warn_unused_result)) DisplayList* finishRecording();
+ WARN_UNUSED_RESULT DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// HWUI Canvas state operations
@@ -211,8 +212,6 @@
virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
float boundsRight, float boundsBottom, float totalAdvance) override;
- virtual void drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint) override;
virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return false; }
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 772aa72..977b53c 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1278,24 +1278,6 @@
float mVOffset;
};
-class DrawPosTextOp : public DrawSomeTextOp {
-public:
- DrawPosTextOp(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint)
- : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
- /* TODO: inherit from DrawBounded and init mLocalBounds */
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawPosText(mText, mBytesCount, mCount, mPositions, mPaint);
- }
-
- virtual const char* name() override { return "DrawPosText"; }
-
-private:
- const float* mPositions;
-};
-
class DrawTextOp : public DrawStrokableOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index e257715..02caaa4 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -35,7 +35,7 @@
#endif
-void Extensions::load() {
+Extensions::Extensions() {
auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS));
mHasNPot = extensions.has("GL_OES_texture_npot");
mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 8ccfabd..67cc747 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -31,7 +31,7 @@
class Extensions {
public:
- void load();
+ Extensions();
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index ccf0b48..5f33cae 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -21,13 +21,20 @@
#include "Extensions.h"
#include "Glop.h"
#include "GlopBuilder.h"
-#include "OpenGLRenderer.h"
#include "PixelBuffer.h"
#include "Rect.h"
#include "renderstate/RenderState.h"
#include "utils/Blur.h"
#include "utils/Timing.h"
+
+#if HWUI_NEW_OPS
+#include "BakedOpState.h"
+#include "BakedOpRenderer.h"
+#else
+#include "OpenGLRenderer.h"
+#endif
+
#include <algorithm>
#include <cutils/properties.h>
#include <SkGlyph.h>
@@ -59,14 +66,25 @@
int transformFlags = pureTranslate
? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
Glop glop;
+#if HWUI_NEW_OPS
+ GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
+ .setRoundRectClipState(bakedState->roundRectClipState)
+ .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
+ .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
+ .setTransform(bakedState->computedState.transform, transformFlags)
+ .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .build();
+ renderer->renderGlop(*bakedState, glop);
+#else
GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
+ .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
.setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
.setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
.setTransform(*(renderer->currentSnapshot()), transformFlags)
.setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
- .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
.build();
renderer->renderGlop(glop);
+#endif
}
///////////////////////////////////////////////////////////////////////////////
@@ -539,7 +557,7 @@
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
+ int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -558,7 +576,7 @@
mBounds = nullptr;
Rect bounds;
- mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
+ mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions);
uint32_t intRadius = Blur::convertRadiusToInt(radius);
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
@@ -590,7 +608,7 @@
// text has non-whitespace, so draw and blur to create the shadow
// NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
// TODO: don't draw pure whitespace in the first place, and avoid needing this check
- mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
+ mCurrentFont->render(paint, text, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
@@ -635,15 +653,15 @@
}
bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
- const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
+ int numGlyphs, int x, int y, const float* positions,
+ Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
+ mCurrentFont->render(paint, text, numGlyphs, x, y, positions);
if (forceFinish) {
finishRender();
@@ -653,15 +671,15 @@
}
bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) {
+ int numGlyphs, const SkPath* path, float hOffset, float vOffset,
+ Rect* bounds, TextDrawFunctor* functor) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
+ mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset);
finishRender();
return mDrawn;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 8172312..87cfe7f 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -44,13 +44,28 @@
namespace android {
namespace uirenderer {
+#if HWUI_NEW_OPS
+class BakedOpState;
+class BakedOpRenderer;
+#else
class OpenGLRenderer;
+#endif
class TextDrawFunctor {
public:
- TextDrawFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
+ TextDrawFunctor(
+#if HWUI_NEW_OPS
+ BakedOpRenderer* renderer,
+ const BakedOpState* bakedState,
+#else
+ OpenGLRenderer* renderer,
+#endif
+ float x, float y, bool pureTranslate,
int alpha, SkXfermode::Mode mode, const SkPaint* paint)
: renderer(renderer)
+#if HWUI_NEW_OPS
+ , bakedState(bakedState)
+#endif
, x(x)
, y(y)
, pureTranslate(pureTranslate)
@@ -61,7 +76,12 @@
void draw(CacheTexture& texture, bool linearFiltering);
+#if HWUI_NEW_OPS
+ BakedOpRenderer* renderer;
+ const BakedOpState* bakedState;
+#else
OpenGLRenderer* renderer;
+#endif
float x;
float y;
bool pureTranslate;
@@ -83,15 +103,13 @@
void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
void endPrecaching();
- // bounds is an out parameter
bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
- Rect* bounds, TextDrawFunctor* functor, bool forceFinish = true);
+ int numGlyphs, int x, int y, const float* positions,
+ Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true);
- // bounds is an out parameter
bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor);
+ int numGlyphs, const SkPath* path,
+ float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor);
struct DropShadow {
uint32_t width;
@@ -103,8 +121,8 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
- DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, float radius, const float* positions);
+ DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs,
+ float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 39cadd1..b117754 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#include <GLES2/gl2.h>
+#include "LayerCache.h"
+
+#include "Caches.h"
+#include "Properties.h"
#include <utils/Log.h>
-#include "Caches.h"
-#include "LayerCache.h"
-#include "Properties.h"
+#include <GLES2/gl2.h>
namespace android {
namespace uirenderer {
@@ -29,15 +30,9 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting layer cache size to %sMB", property);
- setMaxSize(MB(atof(property)));
- } else {
- INIT_LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
- }
-}
+LayerCache::LayerCache()
+ : mSize(0)
+ , mMaxSize(Properties::layerPoolSize) {}
LayerCache::~LayerCache() {
clear();
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 68f80ea..5e954ae 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -18,11 +18,13 @@
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "utils/FatVector.h"
#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
#include <SkCanvas.h>
-#include <utils/Trace.h>
+#include <SkPathOps.h>
#include <utils/TypeHelpers.h>
namespace android {
@@ -32,8 +34,8 @@
public:
BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
- : mBatchId(batchId)
- , mMerging(merging) {
+ : mBatchId(batchId)
+ , mMerging(merging) {
mBounds = op->computedState.clippedBounds;
mOps.push_back(op);
}
@@ -206,9 +208,10 @@
};
OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
: width(width)
, height(height)
+ , repaintRect(repaintRect)
, offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
, beginLayerOp(beginLayerOp)
, renderNode(renderNode) {}
@@ -308,15 +311,19 @@
OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes)
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
- mLayerStack.push_back(0);
+ mLayerReorderers.reserve(layers.entries().size());
+ mLayerStack.reserve(layers.entries().size());
+
+ // Prepare to defer Fbo0
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+ mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
- Vector3());
+ lightCenter);
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
// updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
@@ -324,12 +331,15 @@
RenderNode* layerNode = layers.entries()[i].renderNode;
const Rect& layerDamage = layers.entries()[i].damage;
- saveForLayer(layerNode->getWidth(), layerNode->getHeight(), nullptr, layerNode);
- mCanvasState.writableSnapshot()->setClip(
- layerDamage.left, layerDamage.top, layerDamage.right, layerDamage.bottom);
+ // map current light center into RenderNode's coordinate space
+ Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
+ layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
+
+ saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
+ layerDamage, lightCenter, nullptr, layerNode);
if (layerNode->getDisplayList()) {
- deferImpl(*(layerNode->getDisplayList()));
+ deferDisplayList(*(layerNode->getDisplayList()));
}
restoreForLayer();
}
@@ -344,16 +354,18 @@
}
}
-OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
+OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
+ const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
-
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+ // Prepare to defer Fbo0
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight,
+ Rect(viewportWidth, viewportHeight));
mLayerStack.push_back(0);
-
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
- 0, 0, viewportWidth, viewportHeight, Vector3());
- deferImpl(displayList);
+ 0, 0, viewportWidth, viewportHeight, lightCenter);
+
+ deferDisplayList(displayList);
}
void OpReorderer::onViewportInitialized() {}
@@ -361,18 +373,99 @@
void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
- if (node.applyViewProperties(mCanvasState, mAllocator)) {
- // not rejected so render
+ const RenderProperties& properties = node.properties();
+ const Outline& outline = properties.getOutline();
+ if (properties.getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties.getScaleX() == 0
+ || properties.getScaleY() == 0) {
+ return; // rejected
+ }
+
+ if (properties.getLeft() != 0 || properties.getTop() != 0) {
+ mCanvasState.translate(properties.getLeft(), properties.getTop());
+ }
+ if (properties.getStaticMatrix()) {
+ mCanvasState.concatMatrix(*properties.getStaticMatrix());
+ } else if (properties.getAnimationMatrix()) {
+ mCanvasState.concatMatrix(*properties.getAnimationMatrix());
+ }
+ if (properties.hasTransformMatrix()) {
+ if (properties.isTransformTranslateOnly()) {
+ mCanvasState.translate(properties.getTranslationX(), properties.getTranslationY());
+ } else {
+ mCanvasState.concatMatrix(*properties.getTransformMatrix());
+ }
+ }
+
+ const int width = properties.getWidth();
+ const int height = properties.getHeight();
+
+ Rect saveLayerBounds; // will be set to non-empty if saveLayer needed
+ const bool isLayer = properties.effectiveLayerType() != LayerType::None;
+ int clipFlags = properties.getClippingFlags();
+ if (properties.getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ }
+ if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+ // simply scale rendering content's alpha
+ mCanvasState.scaleAlpha(properties.getAlpha());
+ } else {
+ // schedule saveLayer by initializing saveLayerBounds
+ saveLayerBounds.set(0, 0, width, height);
+ if (clipFlags) {
+ properties.getClippingRectForFlags(clipFlags, &saveLayerBounds);
+ clipFlags = 0; // all clipping done by savelayer
+ }
+ }
+
+ if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
+ // pretend alpha always causes savelayer to warn about
+ // performance problem affecting old versions
+ ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", node.getName(), width, height);
+ }
+ }
+ if (clipFlags) {
+ Rect clipRect;
+ properties.getClippingRectForFlags(clipFlags, &clipRect);
+ mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+ SkRegion::kIntersect_Op);
+ }
+
+ if (properties.getRevealClip().willClip()) {
+ Rect bounds;
+ properties.getRevealClip().getBounds(&bounds);
+ mCanvasState.setClippingRoundRect(mAllocator,
+ bounds, properties.getRevealClip().getRadius());
+ } else if (properties.getOutline().willClip()) {
+ mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
+ }
+
+ if (!mCanvasState.quickRejectConservative(0, 0, width, height)) {
+ // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
if (node.getLayer()) {
// HW layer
LayerOp* drawLayerOp = new (mAllocator) LayerOp(node);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
- // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+ // Node's layer already deferred, schedule it to render into parent layer
currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
}
+ } else if (CC_UNLIKELY(!saveLayerBounds.isEmpty())) {
+ // draw DisplayList contents within temporary, since persisted layer could not be used.
+ // (temp layers are clipped to viewport, since they don't persist offscreen content)
+ SkPaint saveLayerPaint;
+ saveLayerPaint.setAlpha(properties.getAlpha());
+ onBeginLayerOp(*new (mAllocator) BeginLayerOp(
+ saveLayerBounds,
+ Matrix4::identity(),
+ saveLayerBounds,
+ &saveLayerPaint));
+ deferDisplayList(*(node.getDisplayList()));
+ onEndLayerOp(*new (mAllocator) EndLayerOp());
} else {
- deferImpl(*(node.getDisplayList()));
+ deferDisplayList(*(node.getDisplayList()));
}
}
}
@@ -462,8 +555,61 @@
}
void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
- // TODO
+ auto& node = *casterNodeOp.renderNode;
+ auto& properties = node.properties();
+
+ if (properties.getAlpha() <= 0.0f
+ || properties.getOutline().getAlpha() <= 0.0f
+ || !properties.getOutline().getPath()
+ || properties.getScaleX() == 0
+ || properties.getScaleY() == 0) {
+ // no shadow to draw
+ return;
+ }
+
+ const SkPath* casterOutlinePath = properties.getOutline().getPath();
+ const SkPath* revealClipPath = properties.getRevealClip().getPath();
+ if (revealClipPath && revealClipPath->isEmpty()) return;
+
+ float casterAlpha = properties.getAlpha() * properties.getOutline().getAlpha();
+
+ // holds temporary SkPath to store the result of intersections
+ SkPath* frameAllocatedPath = nullptr;
+ const SkPath* casterPath = casterOutlinePath;
+
+ // intersect the shadow-casting path with the reveal, if present
+ if (revealClipPath) {
+ frameAllocatedPath = createFrameAllocatedPath();
+
+ Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
+ casterPath = frameAllocatedPath;
+ }
+
+ // intersect the shadow-casting path with the clipBounds, if present
+ if (properties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
+ if (!frameAllocatedPath) {
+ frameAllocatedPath = createFrameAllocatedPath();
+ }
+ Rect clipBounds;
+ properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+ SkPath clipBoundsPath;
+ clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
+ clipBounds.right, clipBounds.bottom);
+
+ Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
+ casterPath = frameAllocatedPath;
+ }
+
+ ShadowOp* shadowOp = new (mAllocator) ShadowOp(casterNodeOp, casterAlpha, casterPath,
+ mCanvasState.getLocalClipBounds(),
+ mCanvasState.currentSnapshot()->getRelativeLightCenter());
+ BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
+ mAllocator, *mCanvasState.currentSnapshot(), shadowOp);
+ if (CC_LIKELY(bakedOpState)) {
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
+ }
}
+
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -472,7 +618,7 @@
*/
#define OP_RECEIVER(Type) \
[](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
-void OpReorderer::deferImpl(const DisplayList& displayList) {
+void OpReorderer::deferDisplayList(const DisplayList& displayList) {
static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
MAP_OPS(OP_RECEIVER)
};
@@ -525,6 +671,13 @@
currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
}
+void OpReorderer::onLinesOp(const LinesOp& op) {
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
+
+}
+
void OpReorderer::onRectOp(const RectOp& op) {
BakedOpState* bakedStateOp = tryBakeOpState(op);
if (!bakedStateOp) return; // quick rejected
@@ -537,17 +690,34 @@
currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
}
-void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
+void OpReorderer::onTextOp(const TextOp& op) {
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ // TODO: better handling of shader (since we won't care about color then)
+ batchid_t batchId = op.paint->getColor() == SK_ColorBLACK
+ ? OpBatchType::Text : OpBatchType::ColorText;
+ mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
+ currentLayer().deferMergeableOp(mAllocator, bakedStateOp, batchId, mergeId);
+}
+
+void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ float contentTranslateX, float contentTranslateY,
+ const Rect& repaintRect,
+ const Vector3& lightCenter,
+ const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
- mCanvasState.writableSnapshot()->transform->loadIdentity();
mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
+ mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
+ mCanvasState.writableSnapshot()->transform->loadTranslate(
+ contentTranslateX, contentTranslateY, 0);
+ mCanvasState.writableSnapshot()->setClip(
+ repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
- // create a new layer, and push its index on the stack
+ // create a new layer repaint, and push its index on the stack
mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back(layerWidth, layerHeight, beginLayerOp, renderNode);
+ mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
}
void OpReorderer::restoreForLayer() {
@@ -558,9 +728,48 @@
// TODO: test rejection at defer time, where the bounds become empty
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
- const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
- const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
- saveForLayer(layerWidth, layerHeight, &op, nullptr);
+ uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+ uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+
+ auto previous = mCanvasState.currentSnapshot();
+ Vector3 lightCenter = previous->getRelativeLightCenter();
+
+ // Combine all transforms used to present saveLayer content:
+ // parent content transform * canvas transform * bounds offset
+ Matrix4 contentTransform(*previous->transform);
+ contentTransform.multiply(op.localMatrix);
+ contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);
+
+ Matrix4 inverseContentTransform;
+ inverseContentTransform.loadInverse(contentTransform);
+
+ // map the light center into layer-relative space
+ inverseContentTransform.mapPoint3d(lightCenter);
+
+ // Clip bounds of temporary layer to parent's clip rect, so:
+ Rect saveLayerBounds(layerWidth, layerHeight);
+ // 1) transform Rect(width, height) into parent's space
+ // note: left/top offsets put in contentTransform above
+ contentTransform.mapRect(saveLayerBounds);
+ // 2) intersect with parent's clip
+ saveLayerBounds.doIntersect(previous->getRenderTargetClip());
+ // 3) and transform back
+ inverseContentTransform.mapRect(saveLayerBounds);
+ saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
+ saveLayerBounds.roundOut();
+
+ // if bounds are reduced, will clip the layer's area by reducing required bounds...
+ layerWidth = saveLayerBounds.getWidth();
+ layerHeight = saveLayerBounds.getHeight();
+ // ...and shifting drawing content to account for left/top side clipping
+ float contentTranslateX = -saveLayerBounds.left;
+ float contentTranslateY = -saveLayerBounds.top;
+
+ saveForLayer(layerWidth, layerHeight,
+ contentTranslateX, contentTranslateY,
+ Rect(layerWidth, layerHeight),
+ lightCenter,
+ &op, nullptr);
}
void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
@@ -593,5 +802,9 @@
LOG_ALWAYS_FATAL("unsupported");
}
+void OpReorderer::onShadowOp(const ShadowOp& op) {
+ LOG_ALWAYS_FATAL("unsupported");
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 936b6ed..976f413 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -51,6 +51,7 @@
AlphaMaskTexture,
Text,
ColorText,
+ Shadow,
Count // must be last
};
@@ -66,13 +67,13 @@
class LayerReorderer {
public:
// Create LayerReorderer for Fbo0
- LayerReorderer(uint32_t width, uint32_t height)
- : LayerReorderer(width, height, nullptr, nullptr) {};
+ LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
+ : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
// Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
// saveLayer, renderNode is present for a HW layer.
LayerReorderer(uint32_t width, uint32_t height,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
@@ -100,6 +101,7 @@
const uint32_t width;
const uint32_t height;
+ const Rect repaintRect;
OffscreenBuffer* offscreenBuffer;
const BeginLayerOp* beginLayerOp;
const RenderNode* renderNode;
@@ -115,14 +117,15 @@
// Maps batch ids to the most recent *non-merging* batch of that id
OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
-
};
+
public:
OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
- const std::vector< sp<RenderNode> >& nodes);
+ const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
- OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+ OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList,
+ const Vector3& lightCenter);
virtual ~OpReorderer() {}
@@ -152,18 +155,18 @@
LayerReorderer& layer = mLayerReorderers[i];
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
- renderer.startLayer(layer.offscreenBuffer);
+ renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
} else if (!layer.empty()) { // save layer - skip entire layer if empty
- layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
+ layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
}
}
const LayerReorderer& fbo0 = mLayerReorderers[0];
- renderer.startFrame(fbo0.width, fbo0.height);
+ renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
fbo0.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endFrame();
}
@@ -187,6 +190,9 @@
Positive
};
void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+ float contentTranslateX, float contentTranslateY,
+ const Rect& repaintRect,
+ const Vector3& lightCenter,
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
@@ -201,7 +207,7 @@
void deferShadow(const RenderNodeOp& casterOp);
- void deferImpl(const DisplayList& displayList);
+ void deferDisplayList(const DisplayList& displayList);
template <typename V>
void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
@@ -210,6 +216,10 @@
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
+ SkPath* createFrameAllocatedPath() {
+ mFrameAllocatedPaths.emplace_back(new SkPath);
+ return mFrameAllocatedPaths.back().get();
+ }
/**
* Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
*
@@ -220,6 +230,8 @@
void on##Type(const Type& op);
MAP_OPS(INTERNAL_OP_HANDLER)
+ std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
+
// List of every deferred layer's render state. Replayed in reverse order to render a frame.
std::vector<LayerReorderer> mLayerReorderers;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 12c4607..e386b1c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1950,7 +1950,7 @@
}
void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
- int bytesCount, int count, const float* positions,
+ int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
@@ -1963,7 +1963,7 @@
// if shader-based correction is enabled
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
ShadowTexture* texture = mCaches.dropShadowCache.get(
- paint, text, bytesCount, count, textShadow.radius, positions);
+ paint, text, count, textShadow.radius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!texture) return;
@@ -1991,57 +1991,6 @@
&& PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
-void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint) {
- if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
- return;
- }
-
- // NOTE: Skia does not support perspective transform on drawPosText yet
- if (!currentTransform()->isSimple()) {
- return;
- }
-
- mRenderState.scissor().setEnabled(true);
-
- float x = 0.0f;
- float y = 0.0f;
- const bool pureTranslate = currentTransform()->isPureTranslate();
- if (pureTranslate) {
- x = floorf(x + currentTransform()->getTranslateX() + 0.5f);
- y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
- }
-
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- fontRenderer.setFont(paint, SkMatrix::I());
-
- int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
-
- if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
- drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
- alpha, 0.0f, 0.0f);
- }
-
- // Pick the appropriate texture filtering
- bool linearFilter = currentTransform()->changesBounds();
- if (pureTranslate && !linearFilter) {
- linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
- }
- fontRenderer.setTextureFiltering(linearFilter);
-
- const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip());
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
- TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
- if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y,
- positions, hasLayer() ? &bounds : nullptr, &functor)) {
- dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
- mDirty = true;
- }
-
-}
-
bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
if (CC_LIKELY(transform.isPureTranslate())) {
outMatrix->setIdentity();
@@ -2166,7 +2115,7 @@
if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
- drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
+ drawTextShadow(paint, text, count, positions, fontRenderer,
alpha, oldX, oldY);
}
@@ -2195,17 +2144,22 @@
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+ TextDrawFunctor functor(nullptr, nullptr, x, y, pureTranslate, alpha, mode, paint);
+#else
TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
+#endif
// don't call issuedrawcommand, do it at end of batch
bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
- status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
+ status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
} else {
- status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
+ status = fontRenderer.renderPosText(paint, clip, text, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
}
@@ -2216,8 +2170,6 @@
dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(totalAdvance, oldX, oldY, paint);
-
mDirty = true;
}
@@ -2236,12 +2188,17 @@
int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+ TextDrawFunctor functor(nullptr, nullptr, 0.0f, 0.0f, false, alpha, mode, paint);
+#else
TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
+#endif
const Rect* clip = &writableSnapshot()->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
- if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
+ if (fontRenderer.renderTextOnPath(paint, clip, text, count, path,
hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
mDirty = true;
@@ -2375,56 +2332,6 @@
renderGlop(glop);
}
-// Same values used by Skia
-#define kStdStrikeThru_Offset (-6.0f / 21.0f)
-#define kStdUnderline_Offset (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y,
- const SkPaint* paint) {
- // Handle underline and strike-through
- uint32_t flags = paint->getFlags();
- if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
- SkPaint paintCopy(*paint);
-
- if (CC_LIKELY(underlineWidth > 0.0f)) {
- const float textSize = paintCopy.getTextSize();
- const float strokeWidth = std::max(textSize * kStdUnderline_Thickness, 1.0f);
-
- const float left = x;
- float top = 0.0f;
-
- int linesCount = 0;
- if (flags & SkPaint::kUnderlineText_Flag) linesCount++;
- if (flags & SkPaint::kStrikeThruText_Flag) linesCount++;
-
- const int pointsCount = 4 * linesCount;
- float points[pointsCount];
- int currentPoint = 0;
-
- if (flags & SkPaint::kUnderlineText_Flag) {
- top = y + textSize * kStdUnderline_Offset;
- points[currentPoint++] = left;
- points[currentPoint++] = top;
- points[currentPoint++] = left + underlineWidth;
- points[currentPoint++] = top;
- }
-
- if (flags & SkPaint::kStrikeThruText_Flag) {
- top = y + textSize * kStdStrikeThru_Offset;
- points[currentPoint++] = left;
- points[currentPoint++] = top;
- points[currentPoint++] = left + underlineWidth;
- points[currentPoint++] = top;
- }
-
- paintCopy.setStrokeWidth(strokeWidth);
-
- drawLines(&points[0], pointsCount, &paintCopy);
- }
- }
-}
-
void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
if (mState.currentlyIgnored()) {
return;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 400c225..84bc9b0 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -193,8 +193,6 @@
void drawPoints(const float* points, int count, const SkPaint* paint);
void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
float hOffset, float vOffset, const SkPaint* paint);
- void drawPosText(const char* text, int bytesCount, int count,
- const float* positions, const SkPaint* paint);
void drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = DrawOpMode::kImmediate);
@@ -637,24 +635,11 @@
*/
void drawConvexPath(const SkPath& path, const SkPaint* paint);
- /**
- * Draws text underline and strike-through if needed.
- *
- * @param text The text to decor
- * @param bytesCount The number of bytes in the text
- * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
- * @param x The x coordinate where the text will be drawn
- * @param y The y coordinate where the text will be drawn
- * @param paint The paint to draw the text with
- */
- void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint);
-
/**
* Draws shadow layer on text (with optional positions).
*
* @param paint The paint to draw the shadow with
* @param text The text to draw
- * @param bytesCount The number of bytes in the text
* @param count The number of glyphs in the text
* @param positions The x, y positions of individual glyphs (or NULL)
* @param fontRenderer The font renderer object
@@ -662,7 +647,7 @@
* @param x The x coordinate where the shadow will be drawn
* @param y The y coordinate where the shadow will be drawn
*/
- void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count,
+ void drawTextShadow(const SkPaint* paint, const char* text, int count,
const float* positions, FontRenderer& fontRenderer, int alpha,
float x, float y);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index e8ed8a1..4e9ac9c 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -18,6 +18,7 @@
#include "jni.h"
+#include <errno.h>
#include <utils/Log.h>
#include <sstream>
#include <stdlib.h>
@@ -97,14 +98,31 @@
*outEndPosition = currentIndex;
}
+static float parseFloat(PathParser::ParseResult* result, const char* startPtr, size_t expectedLength) {
+ char* endPtr = NULL;
+ float currentValue = strtof(startPtr, &endPtr);
+ if ((currentValue == HUGE_VALF || currentValue == -HUGE_VALF) && errno == ERANGE) {
+ result->failureOccurred = true;
+ result->failureMessage = "Float out of range: ";
+ result->failureMessage.append(startPtr, expectedLength);
+ }
+ if (currentValue == 0 && endPtr == startPtr) {
+ // No conversion is done.
+ result->failureOccurred = true;
+ result->failureMessage = "Float format error when parsing: ";
+ result->failureMessage.append(startPtr, expectedLength);
+ }
+ return currentValue;
+}
+
/**
-* Parse the floats in the string.
-* This is an optimized version of parseFloat(s.split(",|\\s"));
-*
-* @param s the string containing a command and list of floats
-* @return array of floats
-*/
-static void getFloats(std::vector<float>* outPoints, const char* pathStr, int start, int end) {
+ * Parse the floats in the string.
+ *
+ * @param s the string containing a command and list of floats
+ * @return true on success
+ */
+static void getFloats(std::vector<float>* outPoints, PathParser::ParseResult* result,
+ const char* pathStr, int start, int end) {
if (pathStr[start] == 'z' || pathStr[start] == 'Z') {
return;
@@ -120,7 +138,12 @@
extract(&endPosition, &endWithNegOrDot, pathStr, startPosition, end);
if (startPosition < endPosition) {
- outPoints->push_back(strtof(&pathStr[startPosition], NULL));
+ float currentValue = parseFloat(result, &pathStr[startPosition],
+ end - startPosition);
+ if (result->failureOccurred) {
+ return;
+ }
+ outPoints->push_back(currentValue);
}
if (endWithNegOrDot) {
@@ -130,10 +153,14 @@
startPosition = endPosition + 1;
}
}
+ return;
}
-void PathParser::getPathDataFromString(PathData* data, const char* pathStr, size_t strLen) {
+void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
+ const char* pathStr, size_t strLen) {
if (pathStr == NULL) {
+ result->failureOccurred = true;
+ result->failureMessage = "Path string cannot be NULL.";
return;
}
@@ -143,7 +170,10 @@
while (end < strLen) {
end = nextStart(pathStr, strLen, end);
std::vector<float> points;
- getFloats(&points, pathStr, start, end);
+ getFloats(&points, result, pathStr, start, end);
+ if (result->failureOccurred) {
+ return;
+ }
data->verbs.push_back(pathStr[start]);
data->verbSizes.push_back(points.size());
data->points.insert(data->points.end(), points.begin(), points.end());
@@ -151,16 +181,11 @@
end++;
}
- if ((end - start) == 1 && pathStr[start] != '\0') {
+ if ((end - start) == 1 && start < strLen) {
data->verbs.push_back(pathStr[start]);
data->verbSizes.push_back(0);
}
-
- int i = 0;
- while(pathStr[i] != '\0') {
- i++;
- }
-
+ return;
}
void PathParser::dump(const PathData& data) {
@@ -169,6 +194,7 @@
for (size_t i = 0; i < data.verbs.size(); i++) {
std::ostringstream os;
os << data.verbs[i];
+ os << ", verb size: " << data.verbSizes[i];
for (size_t j = 0; j < data.verbSizes[i]; j++) {
os << " " << data.points[start + j];
}
@@ -183,10 +209,20 @@
ALOGD("points are : %s", os.str().c_str());
}
-void PathParser::parseStringForSkPath(SkPath* skPath, const char* pathStr, size_t strLen) {
+void PathParser::parseStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
PathData pathData;
- getPathDataFromString(&pathData, pathStr, strLen);
- VectorDrawablePath::verbsToPath(skPath, &pathData);
+ getPathDataFromString(&pathData, result, pathStr, strLen);
+ if (result->failureOccurred) {
+ return;
+ }
+ // Check if there is valid data coming out of parsing the string.
+ if (pathData.verbs.size() == 0) {
+ result->failureOccurred = true;
+ result->failureMessage = "No verbs found in the string for pathData";
+ return;
+ }
+ VectorDrawableUtils::verbsToPath(skPath, pathData);
+ return;
}
}; // namespace uirenderer
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 6dc7ee1..4c87b18 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -18,17 +18,31 @@
#define ANDROID_HWUI_PATHPARSER_H
#include "VectorDrawablePath.h"
+#include "utils/VectorDrawableUtils.h"
#include <jni.h>
#include <android/log.h>
+#include <cutils/compiler.h>
+
+#include <string>
namespace android {
namespace uirenderer {
+
class PathParser {
public:
- static void parseStringForSkPath(SkPath* outPath, const char* pathStr, size_t strLength);
- static void getPathDataFromString(PathData* outData, const char* pathStr, size_t strLength);
+ struct ANDROID_API ParseResult {
+ bool failureOccurred = false;
+ std::string failureMessage;
+ };
+ /**
+ * Parse the string literal and create a Skia Path. Return true on success.
+ */
+ ANDROID_API static void parseStringForSkPath(SkPath* outPath, ParseResult* result,
+ const char* pathStr, size_t strLength);
+ ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
+ const char* pathStr, size_t strLength);
static void dump(const PathData& data);
};
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e818186..0669596 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -37,6 +37,7 @@
bool Properties::enablePartialUpdates = true;
float Properties::textGamma = DEFAULT_TEXT_GAMMA;
+int Properties::layerPoolSize = DEFAULT_LAYER_CACHE_SIZE;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
@@ -52,10 +53,19 @@
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+static int property_get_int(const char* key, int defaultValue) {
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+ if (property_get(key, buf, "") > 0) {
+ return atoi(buf);
+ }
+ return defaultValue;
+}
+
static float property_get_float(const char* key, float defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
- if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+ if (property_get(key, buf, "") > 0) {
return atof(buf);
}
return defaultValue;
@@ -114,16 +124,14 @@
showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
- debugLevel = kDebugDisabled;
- if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) {
- debugLevel = (DebugLevel) atoi(property);
- }
+ debugLevel = (DebugLevel) property_get_int(PROPERTY_DEBUG, kDebugDisabled);
skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA);
+ layerPoolSize = MB(property_get_float(PROPERTY_LAYER_CACHE_SIZE, DEFAULT_LAYER_CACHE_SIZE));
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1293c78..1dde7e0 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -267,6 +267,8 @@
static float textGamma;
+ static int layerPoolSize;
+
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
static StencilClipDebug debugStencilClip;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 04af8e3..127dca5 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -17,10 +17,12 @@
#ifndef ANDROID_HWUI_RECORDED_OP_H
#define ANDROID_HWUI_RECORDED_OP_H
-#include "utils/LinearAllocator.h"
-#include "Rect.h"
+#include "font/FontUtil.h"
#include "Matrix.h"
+#include "Rect.h"
#include "RenderNode.h"
+#include "utils/LinearAllocator.h"
+#include "Vector.h"
#include "SkXfermode.h"
@@ -41,9 +43,12 @@
*/
#define MAP_OPS(OP_FN) \
OP_FN(BitmapOp) \
+ OP_FN(LinesOp) \
OP_FN(RectOp) \
OP_FN(RenderNodeOp) \
+ OP_FN(ShadowOp) \
OP_FN(SimpleRectsOp) \
+ OP_FN(TextOp) \
OP_FN(BeginLayerOp) \
OP_FN(EndLayerOp) \
OP_FN(LayerOp)
@@ -96,6 +101,10 @@
bool skipInOrderDraw = false;
};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Standard Ops
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
struct BitmapOp : RecordedOp {
BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
: SUPER(BitmapOp)
@@ -104,11 +113,48 @@
// TODO: asset atlas/texture id lookup?
};
+struct LinesOp : RecordedOp {
+ LinesOp(BASE_PARAMS, const float* points, const int floatCount)
+ : SUPER(LinesOp)
+ , points(points)
+ , floatCount(floatCount) {}
+ const float* points;
+ const int floatCount;
+};
+
struct RectOp : RecordedOp {
RectOp(BASE_PARAMS)
: SUPER(RectOp) {}
};
+/**
+ * Real-time, dynamic-lit shadow.
+ *
+ * Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time,
+ * and are resolved dynamically, and transform isn't needed.
+ *
+ * State construction handles these properties specially, ignoring matrix/bounds.
+ */
+struct ShadowOp : RecordedOp {
+ ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath,
+ const Rect& clipRect, const Vector3& lightCenter)
+ : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr)
+ , shadowMatrixXY(casterOp.localMatrix)
+ , shadowMatrixZ(casterOp.localMatrix)
+ , casterAlpha(casterAlpha)
+ , casterPath(casterPath)
+ , lightCenter(lightCenter) {
+ const RenderNode& node = *casterOp.renderNode;
+ node.applyViewPropertyTransforms(shadowMatrixXY, false);
+ node.applyViewPropertyTransforms(shadowMatrixZ, true);
+ };
+ Matrix4 shadowMatrixXY;
+ Matrix4 shadowMatrixZ;
+ const float casterAlpha;
+ const SkPath* casterPath;
+ const Vector3 lightCenter;
+};
+
struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount)
: SUPER(SimpleRectsOp)
@@ -118,6 +164,27 @@
const size_t vertexCount;
};
+struct TextOp : RecordedOp {
+ TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount,
+ float x, float y)
+ : SUPER(TextOp)
+ , glyphs(glyphs)
+ , positions(positions)
+ , glyphCount(glyphCount)
+ , x(x)
+ , y(y) {}
+ const glyph_t* glyphs;
+ const float* positions;
+ const int glyphCount;
+ const float x;
+ const float y;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Layers
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
/**
* Stateful operation! denotes the creation of an off-screen layer,
* and that commands following will render into it.
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index e988555..61fa384 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -156,7 +156,7 @@
snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
- snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+ snapshot.transform->loadTranslate(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
Rect clip = layerBounds;
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
@@ -230,12 +230,9 @@
void RecordingCanvas::drawPaint(const SkPaint& paint) {
// TODO: more efficient recording?
- Matrix4 identity;
- identity.loadIdentity();
-
addOp(new (alloc()) RectOp(
mState.getRenderTargetClipBounds(),
- identity,
+ Matrix4::identity(),
mState.getRenderTargetClipBounds(),
refPaint(&paint)));
}
@@ -244,9 +241,30 @@
void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
LOG_ALWAYS_FATAL("TODO!");
}
-void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+
+void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
+ if (floatCount < 4) return;
+ floatCount &= ~0x3; // round down to nearest four
+
+ Rect unmappedBounds(points[0], points[1], points[0], points[1]);
+ for (int i = 2; i < floatCount; i += 2) {
+ unmappedBounds.left = std::min(unmappedBounds.left, points[i]);
+ unmappedBounds.right = std::max(unmappedBounds.right, points[i]);
+ unmappedBounds.top = std::min(unmappedBounds.top, points[i + 1]);
+ unmappedBounds.bottom = std::max(unmappedBounds.bottom, points[i + 1]);
+ }
+
+ // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
+ // 1.0 stroke, treat 1.0 as minimum.
+ unmappedBounds.outset(std::max(paint.getStrokeWidth(), 1.0f) * 0.5f);
+
+ addOp(new (alloc()) LinesOp(
+ unmappedBounds,
+ *mState.currentSnapshot()->transform,
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
}
+
void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
addOp(new (alloc()) RectOp(
Rect(left, top, right, bottom),
@@ -388,17 +406,24 @@
}
// Text
-void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count,
+void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int glyphCount,
const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
float boundsRight, float boundsBottom, float totalAdvance) {
- LOG_ALWAYS_FATAL("TODO!");
+ if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+ glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
+ positions = refBuffer<float>(positions, glyphCount * 2);
+
+ addOp(new (alloc()) TextOp(
+ Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint), glyphs, positions, glyphCount, x, y));
+ drawTextDecorations(x, y, totalAdvance, paint);
}
-void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
-}
+
void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) {
+ // NOTE: can't use refPaint() directly, since it forces left alignment
LOG_ALWAYS_FATAL("TODO!");
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index fc84c98..736cc9e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -20,11 +20,12 @@
#include "Canvas.h"
#include "CanvasState.h"
#include "DisplayList.h"
-#include "utils/LinearAllocator.h"
-#include "utils/NinePatch.h"
#include "ResourceCache.h"
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
+#include "utils/LinearAllocator.h"
+#include "utils/Macros.h"
+#include "utils/NinePatch.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>
@@ -49,7 +50,7 @@
virtual ~RecordingCanvas();
void reset(int width, int height);
- __attribute__((warn_unused_result)) DisplayList* finishRecording();
+ WARN_UNUSED_RESULT DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
@@ -177,8 +178,6 @@
virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
float boundsRight, float boundsBottom, float totalAdvance) override;
- virtual void drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint) override;
virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return false; }
@@ -220,6 +219,15 @@
return cachedPath;
}
+ /**
+ * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in (with deduping
+ * based on paint generation ID)
+ *
+ * Note that this forces Left_Align, since drawText glyph rendering expects left alignment,
+ * since alignment offsetting has been done at a higher level. This is done to essentially all
+ * copied paints, since the deduping can mean a paint is shared by drawText commands and other
+ * types (which wouldn't care about alignment).
+ */
inline const SkPaint* refPaint(const SkPaint* paint) {
if (!paint) return nullptr;
@@ -238,10 +246,11 @@
// In the unlikely event that 2 unique paints have the same hash we do a
// object equality check to ensure we don't erroneously dedup them.
if (cachedPaint == nullptr || *cachedPaint != *paint) {
- cachedPaint = new SkPaint(*paint);
- std::unique_ptr<const SkPaint> copy(cachedPaint);
- mDisplayList->paints.push_back(std::move(copy));
+ SkPaint* copy = new SkPaint(*paint);
+ copy->setTextAlign(SkPaint::kLeft_Align);
+ cachedPaint = copy;
+ mDisplayList->paints.emplace_back(copy);
// replaceValueFor() performs an add if the entry doesn't exist
mPaintMap.replaceValueFor(key, cachedPaint);
refBitmapsInShader(cachedPaint->getShader());
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 50199db..472aad7 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -260,13 +260,6 @@
bottom = std::max(bottom, y);
}
- void expandToCoverRect(float otherLeft, float otherTop, float otherRight, float otherBottom) {
- left = std::min(left, otherLeft);
- top = std::min(top, otherTop);
- right = std::max(right, otherRight);
- bottom = std::max(bottom, otherBottom);
- }
-
SkRect toSkRect() const {
return SkRect::MakeLTRB(left, top, right, bottom);
}
@@ -276,7 +269,7 @@
}
void dump(const char* label = nullptr) const {
- ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
+ ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom);
}
}; // class Rect
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 15ca718..3f24f44 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -248,22 +248,31 @@
}
}
-layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+static layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
#if HWUI_NEW_OPS
- return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
+ return renderState.layerPool().get(renderState, width, height);
#else
return LayerRenderer::createRenderLayer(renderState, width, height);
#endif
}
-void destroyLayer(layer_t* layer) {
+static void destroyLayer(layer_t* layer) {
#if HWUI_NEW_OPS
- BakedOpRenderer::destroyOffscreenBuffer(layer);
+ RenderState& renderState = layer->renderState;
+ renderState.layerPool().putOrDelete(layer);
#else
LayerRenderer::destroyLayer(layer);
#endif
}
+static bool layerMatchesWidthAndHeight(layer_t* layer, int width, int height) {
+#if HWUI_NEW_OPS
+ return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
+#else
+ return layer->layer.getWidth() == width && layer->layer.getHeight() == height;
+#endif
+}
+
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -278,17 +287,19 @@
bool transformUpdateNeeded = false;
if (!mLayer) {
- mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
- damageSelf(info);
- transformUpdateNeeded = true;
+ mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+#if !HWUI_NEW_OPS
+ applyLayerPropertiesToLayer(info);
+#endif
+ damageSelf(info);
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
#if HWUI_NEW_OPS
- } else if (mLayer->viewportWidth != (uint32_t) getWidth()
- || mLayer->viewportHeight != (uint32_t)getHeight()) {
- // TODO: allow node's layer to grow larger
- if ((uint32_t)getWidth() > mLayer->texture.width
- || (uint32_t)getHeight() > mLayer->texture.height) {
+ RenderState& renderState = mLayer->renderState;
+ if (properties().fitsOnLayer()) {
+ mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
+ } else {
#else
- } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
#endif
destroyLayer(mLayer);
@@ -318,15 +329,11 @@
return;
}
- if (transformUpdateNeeded) {
+ if (transformUpdateNeeded && mLayer) {
// update the transform in window of the layer to reset its origin wrt light source position
Matrix4 windowTransform;
info.damageAccumulator->computeCurrentTransform(&windowTransform);
-#if HWUI_NEW_OPS
- // TODO: update layer transform (perhaps as part of enqueueLayerWithDamage)
-#else
mLayer->setWindowTransform(windowTransform);
-#endif
}
#if HWUI_NEW_OPS
@@ -520,76 +527,6 @@
}
}
-bool RenderNode::applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const {
- const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0
- || (outline.getShouldClip() && outline.isEmpty())
- || properties().getScaleX() == 0
- || properties().getScaleY() == 0) {
- return false; // rejected
- }
-
- if (properties().getLeft() != 0 || properties().getTop() != 0) {
- canvasState.translate(properties().getLeft(), properties().getTop());
- }
- if (properties().getStaticMatrix()) {
- canvasState.concatMatrix(*properties().getStaticMatrix());
- } else if (properties().getAnimationMatrix()) {
- canvasState.concatMatrix(*properties().getAnimationMatrix());
- }
- if (properties().hasTransformMatrix()) {
- if (properties().isTransformTranslateOnly()) {
- canvasState.translate(properties().getTranslationX(), properties().getTranslationY());
- } else {
- canvasState.concatMatrix(*properties().getTransformMatrix());
- }
- }
-
- const bool isLayer = properties().effectiveLayerType() != LayerType::None;
- int clipFlags = properties().getClippingFlags();
- if (properties().getAlpha() < 1) {
- if (isLayer) {
- clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
- }
- if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
- // simply scale rendering content's alpha
- canvasState.scaleAlpha(properties().getAlpha());
- } else {
- // savelayer needed to create an offscreen buffer
- Rect layerBounds(0, 0, getWidth(), getHeight());
- if (clipFlags) {
- properties().getClippingRectForFlags(clipFlags, &layerBounds);
- clipFlags = 0; // all clipping done by savelayer
- }
- LOG_ALWAYS_FATAL("TODO: savelayer");
- }
-
- if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
- // pretend alpha always causes savelayer to warn about
- // performance problem affecting old versions
- ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(), getWidth(), getHeight());
- }
- }
- if (clipFlags) {
- Rect clipRect;
- properties().getClippingRectForFlags(clipFlags, &clipRect);
- canvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkRegion::kIntersect_Op);
- }
-
- // TODO: support nesting round rect clips
- if (mProperties.getRevealClip().willClip()) {
- Rect bounds;
- mProperties.getRevealClip().getBounds(&bounds);
- canvasState.setClippingRoundRect(allocator,
- bounds, mProperties.getRevealClip().getRadius());
- } else if (mProperties.getOutline().willClip()) {
- canvasState.setClippingOutline(allocator, &(mProperties.getOutline()));
- }
- return !canvasState.quickRejectConservative(
- 0, 0, properties().getWidth(), properties().getHeight());
-}
-
/*
* For property operations, we pass a savecount of 0, since the operations aren't part of the
* displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index bae5ebe..83d1b58 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -187,9 +187,6 @@
AnimatorManager& animators() { return mAnimatorManager; }
- // Returns false if the properties dictate the subtree contained in this RenderNode won't render
- bool applyViewProperties(CanvasState& canvasState, LinearAllocator& allocator) const;
-
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
bool nothingToDraw() const {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ca7789e..3952798 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -608,12 +608,15 @@
&& getOutline().getAlpha() != 0.0f;
}
- bool promotedToLayer() const {
+ bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
- LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized");
+ return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+ }
+
+ bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None
- && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
- && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
+ && fitsOnLayer()
&& (mComputedFields.mNeedLayerForFunctors
|| (!MathUtils::isZero(mPrimitiveFields.mAlpha)
&& mPrimitiveFields.mAlpha < 1
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 6d3dfac..96c1a7c 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -131,8 +131,6 @@
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) override;
- virtual void drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint) override;
virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) override;
@@ -152,7 +150,6 @@
void drawPoints(const float* points, int count, const SkPaint& paint,
SkCanvas::PointMode mode);
- void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
SkAutoTUnref<SkCanvas> mCanvas;
std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
@@ -712,22 +709,7 @@
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
-}
-
-void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount,
- const SkPaint& paint) {
- SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
- int indx;
- for (indx = 0; indx < posCount; indx++) {
- posPtr[indx].fX = positions[indx << 1];
- posPtr[indx].fY = positions[(indx << 1) + 1];
- }
-
- SkPaint paintCopy(paint);
- paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
- mCanvas->drawPosText(text, count, posPtr, paintCopy);
-
- delete[] posPtr;
+ drawTextDecorations(x, y, totalAdvance, paint);
}
void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 0a58f4b..2f535bb 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -130,6 +130,9 @@
///////////////////////////////////////////////////////////////////////////////
void Snapshot::resetTransform(float x, float y, float z) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("not supported - light center managed differently");
+#else
// before resetting, map current light pos with inverse of current transform
Vector3 center = mRelativeLightCenter;
mat4 inverse;
@@ -139,6 +142,7 @@
transform = &mTransformRoot;
transform->loadTranslate(x, y, z);
+#endif
}
void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index bdce73c..759e39b 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -741,7 +741,7 @@
// vertex's location.
int newPenumbraNumber = indexDelta - 1;
- float accumulatedDeltaLength[newPenumbraNumber];
+ float accumulatedDeltaLength[indexDelta];
float totalDeltaLength = 0;
// To save time, cache the previous umbra vertex info outside the loop
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index b7a76ba..996ac8e 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -30,8 +30,7 @@
///////////////////////////////////////////////////////////////////////////////
hash_t ShadowText::hash() const {
- uint32_t charCount = len / sizeof(char16_t);
- uint32_t hash = JenkinsHashMix(0, len);
+ uint32_t hash = JenkinsHashMix(0, glyphCount);
hash = JenkinsHashMix(hash, android::hash_type(radius));
hash = JenkinsHashMix(hash, android::hash_type(textSize));
hash = JenkinsHashMix(hash, android::hash_type(typeface));
@@ -40,10 +39,10 @@
hash = JenkinsHashMix(hash, android::hash_type(scaleX));
if (text) {
hash = JenkinsHashMixShorts(
- hash, reinterpret_cast<const uint16_t*>(text), charCount);
+ hash, reinterpret_cast<const uint16_t*>(text), glyphCount);
}
if (positions) {
- for (uint32_t i = 0; i < charCount * 2; i++) {
+ for (uint32_t i = 0; i < glyphCount * 2; i++) {
hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
}
}
@@ -51,7 +50,7 @@
}
int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
- int deltaInt = int(lhs.len) - int(rhs.len);
+ int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount);
if (deltaInt != 0) return deltaInt;
deltaInt = lhs.flags - rhs.flags;
@@ -76,7 +75,7 @@
if (!lhs.text) return -1;
if (!rhs.text) return +1;
- deltaInt = memcmp(lhs.text, rhs.text, lhs.len);
+ deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t));
if (deltaInt != 0) return deltaInt;
}
@@ -84,7 +83,7 @@
if (!lhs.positions) return -1;
if (!rhs.positions) return +1;
- return memcmp(lhs.positions, rhs.positions, lhs.len << 2);
+ return memcmp(lhs.positions, rhs.positions, lhs.glyphCount << 1);
}
return 0;
@@ -168,16 +167,16 @@
mCache.clear();
}
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len,
- int numGlyphs, float radius, const float* positions) {
- ShadowText entry(paint, radius, len, text, positions);
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs,
+ float radius, const float* positions) {
+ ShadowText entry(paint, radius, numGlyphs * 2, glyphs, positions);
ShadowTexture* texture = mCache.get(entry);
if (!texture) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
- FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0,
- len, numGlyphs, radius, positions);
+ FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs,
+ radius, positions);
if (!shadow.image) {
return nullptr;
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index caf089f..c4f3c5d 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -34,14 +34,14 @@
class FontRenderer;
struct ShadowText {
- ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
+ ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
}
// len is the number of bytes in text
- ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText,
+ ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText,
const float* positions):
- len(len), radius(radius), positions(positions) {
+ glyphCount(glyphCount), radius(radius), positions(positions) {
// TODO: Propagate this through the API, we should not cast here
text = (const char16_t*) srcText;
@@ -73,17 +73,16 @@
}
void copyTextLocally() {
- uint32_t charCount = len / sizeof(char16_t);
- str.setTo((const char16_t*) text, charCount);
+ str.setTo((const char16_t*) text, glyphCount);
text = str.string();
if (positions != nullptr) {
positionsCopy.clear();
- positionsCopy.appendArray(positions, charCount * 2);
+ positionsCopy.appendArray(positions, glyphCount * 2);
positions = positionsCopy.array();
}
}
- uint32_t len;
+ uint32_t glyphCount;
float radius;
float textSize;
SkTypeface* typeface;
@@ -136,7 +135,7 @@
*/
void operator()(ShadowText& text, ShadowTexture*& texture) override;
- ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len,
+ ShadowTexture* get(const SkPaint* paint, const char* text,
int numGlyphs, float radius, const float* positions);
/**
diff --git a/libs/hwui/VectorDrawablePath.cpp b/libs/hwui/VectorDrawablePath.cpp
index 115435c..c9a54ca 100644
--- a/libs/hwui/VectorDrawablePath.cpp
+++ b/libs/hwui/VectorDrawablePath.cpp
@@ -17,6 +17,7 @@
#include "VectorDrawablePath.h"
#include "PathParser.h"
+#include "utils/VectorDrawableUtils.h"
#include <math.h>
#include <utils/Log.h>
@@ -24,473 +25,35 @@
namespace android {
namespace uirenderer {
-class PathResolver {
-public:
- float currentX = 0;
- float currentY = 0;
- float ctrlPointX = 0;
- float ctrlPointY = 0;
- float currentSegmentStartX = 0;
- float currentSegmentStartY = 0;
- void addCommand(SkPath* outPath, char previousCmd,
- char cmd, const std::vector<float>* points, size_t start, size_t end);
-};
VectorDrawablePath::VectorDrawablePath(const char* pathStr, size_t strLength) {
- PathParser::getPathDataFromString(&mData, pathStr, strLength);
- verbsToPath(&mSkPath, &mData);
+ PathParser::ParseResult result;
+ PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
+ if (!result.failureOccurred) {
+ VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+ }
}
VectorDrawablePath::VectorDrawablePath(const PathData& data) {
mData = data;
// Now we need to construct a path
- verbsToPath(&mSkPath, &data);
+ VectorDrawableUtils::verbsToPath(&mSkPath, data);
}
VectorDrawablePath::VectorDrawablePath(const VectorDrawablePath& path) {
mData = path.mData;
- verbsToPath(&mSkPath, &mData);
+ VectorDrawableUtils::verbsToPath(&mSkPath, mData);
}
-bool VectorDrawablePath::canMorph(const PathData& morphTo) {
- if (mData.verbs.size() != morphTo.verbs.size()) {
- return false;
- }
- for (unsigned int i = 0; i < mData.verbs.size(); i++) {
- if (mData.verbs[i] != morphTo.verbs[i]
- || mData.verbSizes[i] != morphTo.verbSizes[i]) {
- return false;
- }
- }
- return true;
+bool VectorDrawablePath::canMorph(const PathData& morphTo) {
+ return VectorDrawableUtils::canMorph(mData, morphTo);
}
bool VectorDrawablePath::canMorph(const VectorDrawablePath& path) {
return canMorph(path.mData);
}
- /**
- * Convert an array of PathVerb to Path.
- */
-void VectorDrawablePath::verbsToPath(SkPath* outPath, const PathData* data) {
- PathResolver resolver;
- char previousCommand = 'm';
- size_t start = 0;
- outPath->reset();
- for (unsigned int i = 0; i < data->verbs.size(); i++) {
- size_t verbSize = data->verbSizes[i];
- resolver.addCommand(outPath, previousCommand, data->verbs[i], &data->points, start,
- start + verbSize - 1u);
- previousCommand = data->verbs[i];
- start += verbSize;
- }
-}
-/**
- * The current PathVerb will be interpolated between the
- * <code>nodeFrom</code> and <code>nodeTo</code> according to the
- * <code>fraction</code>.
- *
- * @param nodeFrom The start value as a PathVerb.
- * @param nodeTo The end value as a PathVerb
- * @param fraction The fraction to interpolate.
- */
-void VectorDrawablePath::interpolatePaths(PathData* outData,
- const PathData* from, const PathData* to, float fraction) {
- outData->points.resize(from->points.size());
- outData->verbSizes = from->verbSizes;
- outData->verbs = from->verbs;
-
- for (size_t i = 0; i < from->points.size(); i++) {
- outData->points[i] = from->points[i] * (1 - fraction) + to->points[i] * fraction;
- }
-}
-
-/**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
-static void arcToBezier(SkPath* p,
- double cx,
- double cy,
- double a,
- double b,
- double e1x,
- double e1y,
- double theta,
- double start,
- double sweep) {
- // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
- // and http://www.spaceroots.org/documents/ellipse/node22.html
-
- // Maximum of 45 degrees per cubic Bezier segment
- int numSegments = ceil(fabs(sweep * 4 / M_PI));
-
- double eta1 = start;
- double cosTheta = cos(theta);
- double sinTheta = sin(theta);
- double cosEta1 = cos(eta1);
- double sinEta1 = sin(eta1);
- double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
- double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
- double anglePerSegment = sweep / numSegments;
- for (int i = 0; i < numSegments; i++) {
- double eta2 = eta1 + anglePerSegment;
- double sinEta2 = sin(eta2);
- double cosEta2 = cos(eta2);
- double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
- double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
- double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
- double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
- double tanDiff2 = tan((eta2 - eta1) / 2);
- double alpha =
- sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
- double q1x = e1x + alpha * ep1x;
- double q1y = e1y + alpha * ep1y;
- double q2x = e2x - alpha * ep2x;
- double q2y = e2y - alpha * ep2y;
-
- p->cubicTo((float) q1x,
- (float) q1y,
- (float) q2x,
- (float) q2y,
- (float) e2x,
- (float) e2y);
- eta1 = eta2;
- e1x = e2x;
- e1y = e2y;
- ep1x = ep2x;
- ep1y = ep2y;
- }
-}
-
-inline double toRadians(float theta) { return theta * M_PI / 180;}
-
-static void drawArc(SkPath* p,
- float x0,
- float y0,
- float x1,
- float y1,
- float a,
- float b,
- float theta,
- bool isMoreThanHalf,
- bool isPositiveArc) {
-
- /* Convert rotation angle from degrees to radians */
- double thetaD = toRadians(theta);
- /* Pre-compute rotation matrix entries */
- double cosTheta = cos(thetaD);
- double sinTheta = sin(thetaD);
- /* Transform (x0, y0) and (x1, y1) into unit space */
- /* using (inverse) rotation, followed by (inverse) scale */
- double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
- double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
- double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
- double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
- /* Compute differences and averages */
- double dx = x0p - x1p;
- double dy = y0p - y1p;
- double xm = (x0p + x1p) / 2;
- double ym = (y0p + y1p) / 2;
- /* Solve for intersecting unit circles */
- double dsq = dx * dx + dy * dy;
- if (dsq == 0.0) {
- ALOGW("Points are coincident");
- return; /* Points are coincident */
- }
- double disc = 1.0 / dsq - 1.0 / 4.0;
- if (disc < 0.0) {
- ALOGW("Points are too far apart %f", dsq);
- float adjust = (float) (sqrt(dsq) / 1.99999);
- drawArc(p, x0, y0, x1, y1, a * adjust,
- b * adjust, theta, isMoreThanHalf, isPositiveArc);
- return; /* Points are too far apart */
- }
- double s = sqrt(disc);
- double sdx = s * dx;
- double sdy = s * dy;
- double cx;
- double cy;
- if (isMoreThanHalf == isPositiveArc) {
- cx = xm - sdy;
- cy = ym + sdx;
- } else {
- cx = xm + sdy;
- cy = ym - sdx;
- }
-
- double eta0 = atan2((y0p - cy), (x0p - cx));
-
- double eta1 = atan2((y1p - cy), (x1p - cx));
-
- double sweep = (eta1 - eta0);
- if (isPositiveArc != (sweep >= 0)) {
- if (sweep > 0) {
- sweep -= 2 * M_PI;
- } else {
- sweep += 2 * M_PI;
- }
- }
-
- cx *= a;
- cy *= b;
- double tcx = cx;
- cx = cx * cosTheta - cy * sinTheta;
- cy = tcx * sinTheta + cy * cosTheta;
-
- arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-}
-
-
-void PathResolver::addCommand(SkPath* outPath, char previousCmd,
- char cmd, const std::vector<float>* points, size_t start, size_t end) {
-
- int incr = 2;
- float reflectiveCtrlPointX;
- float reflectiveCtrlPointY;
-
- switch (cmd) {
- case 'z':
- case 'Z':
- outPath->close();
- // Path is closed here, but we need to move the pen to the
- // closed position. So we cache the segment's starting position,
- // and restore it here.
- currentX = currentSegmentStartX;
- currentY = currentSegmentStartY;
- ctrlPointX = currentSegmentStartX;
- ctrlPointY = currentSegmentStartY;
- outPath->moveTo(currentX, currentY);
- break;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- incr = 2;
- break;
- case 'h':
- case 'H':
- case 'v':
- case 'V':
- incr = 1;
- break;
- case 'c':
- case 'C':
- incr = 6;
- break;
- case 's':
- case 'S':
- case 'q':
- case 'Q':
- incr = 4;
- break;
- case 'a':
- case 'A':
- incr = 7;
- break;
- }
-
- for (unsigned int k = start; k <= end; k += incr) {
- switch (cmd) {
- case 'm': // moveto - Start a new sub-path (relative)
- currentX += points->at(k + 0);
- currentY += points->at(k + 1);
- if (k > start) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- outPath->rLineTo(points->at(k + 0), points->at(k + 1));
- } else {
- outPath->rMoveTo(points->at(k + 0), points->at(k + 1));
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'M': // moveto - Start a new sub-path
- currentX = points->at(k + 0);
- currentY = points->at(k + 1);
- if (k > start) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- outPath->lineTo(points->at(k + 0), points->at(k + 1));
- } else {
- outPath->moveTo(points->at(k + 0), points->at(k + 1));
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'l': // lineto - Draw a line from the current point (relative)
- outPath->rLineTo(points->at(k + 0), points->at(k + 1));
- currentX += points->at(k + 0);
- currentY += points->at(k + 1);
- break;
- case 'L': // lineto - Draw a line from the current point
- outPath->lineTo(points->at(k + 0), points->at(k + 1));
- currentX = points->at(k + 0);
- currentY = points->at(k + 1);
- break;
- case 'h': // horizontal lineto - Draws a horizontal line (relative)
- outPath->rLineTo(points->at(k + 0), 0);
- currentX += points->at(k + 0);
- break;
- case 'H': // horizontal lineto - Draws a horizontal line
- outPath->lineTo(points->at(k + 0), currentY);
- currentX = points->at(k + 0);
- break;
- case 'v': // vertical lineto - Draws a vertical line from the current point (r)
- outPath->rLineTo(0, points->at(k + 0));
- currentY += points->at(k + 0);
- break;
- case 'V': // vertical lineto - Draws a vertical line from the current point
- outPath->lineTo(currentX, points->at(k + 0));
- currentY = points->at(k + 0);
- break;
- case 'c': // curveto - Draws a cubic Bézier curve (relative)
- outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
- points->at(k + 4), points->at(k + 5));
-
- ctrlPointX = currentX + points->at(k + 2);
- ctrlPointY = currentY + points->at(k + 3);
- currentX += points->at(k + 4);
- currentY += points->at(k + 5);
-
- break;
- case 'C': // curveto - Draws a cubic Bézier curve
- outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
- points->at(k + 4), points->at(k + 5));
- currentX = points->at(k + 4);
- currentY = points->at(k + 5);
- ctrlPointX = points->at(k + 2);
- ctrlPointY = points->at(k + 3);
- break;
- case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- points->at(k + 0), points->at(k + 1),
- points->at(k + 2), points->at(k + 3));
- ctrlPointX = currentX + points->at(k + 0);
- ctrlPointY = currentY + points->at(k + 1);
- currentX += points->at(k + 2);
- currentY += points->at(k + 3);
- break;
- case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
- ctrlPointX = points->at(k + 0);
- ctrlPointY = points->at(k + 1);
- currentX = points->at(k + 2);
- currentY = points->at(k + 3);
- break;
- case 'q': // Draws a quadratic Bézier (relative)
- outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
- ctrlPointX = currentX + points->at(k + 0);
- ctrlPointY = currentY + points->at(k + 1);
- currentX += points->at(k + 2);
- currentY += points->at(k + 3);
- break;
- case 'Q': // Draws a quadratic Bézier
- outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
- ctrlPointX = points->at(k + 0);
- ctrlPointY = points->at(k + 1);
- currentX = points->at(k + 2);
- currentY = points->at(k + 3);
- break;
- case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- points->at(k + 0), points->at(k + 1));
- ctrlPointX = currentX + reflectiveCtrlPointX;
- ctrlPointY = currentY + reflectiveCtrlPointY;
- currentX += points->at(k + 0);
- currentY += points->at(k + 1);
- break;
- case 'T': // Draws a quadratic Bézier curve (reflective control point)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- points->at(k + 0), points->at(k + 1));
- ctrlPointX = reflectiveCtrlPointX;
- ctrlPointY = reflectiveCtrlPointY;
- currentX = points->at(k + 0);
- currentY = points->at(k + 1);
- break;
- case 'a': // Draws an elliptical arc
- // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
- drawArc(outPath,
- currentX,
- currentY,
- points->at(k + 5) + currentX,
- points->at(k + 6) + currentY,
- points->at(k + 0),
- points->at(k + 1),
- points->at(k + 2),
- points->at(k + 3) != 0,
- points->at(k + 4) != 0);
- currentX += points->at(k + 5);
- currentY += points->at(k + 6);
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- case 'A': // Draws an elliptical arc
- drawArc(outPath,
- currentX,
- currentY,
- points->at(k + 5),
- points->at(k + 6),
- points->at(k + 0),
- points->at(k + 1),
- points->at(k + 2),
- points->at(k + 3) != 0,
- points->at(k + 4) != 0);
- currentX = points->at(k + 5);
- currentY = points->at(k + 6);
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- }
- previousCmd = cmd;
- }
-}
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/VectorDrawablePath.h b/libs/hwui/VectorDrawablePath.h
index 40ce986..2e56349 100644
--- a/libs/hwui/VectorDrawablePath.h
+++ b/libs/hwui/VectorDrawablePath.h
@@ -17,15 +17,14 @@
#ifndef ANDROID_HWUI_VPATH_H
#define ANDROID_HWUI_VPATH_H
+#include <cutils/compiler.h>
#include "SkPath.h"
#include <vector>
namespace android {
namespace uirenderer {
-
-
-struct PathData {
+struct ANDROID_API PathData {
// TODO: Try using FatVector instead of std::vector and do a micro benchmark on the performance
// difference.
std::vector<char> verbs;
@@ -44,9 +43,7 @@
VectorDrawablePath(const char* path, size_t strLength);
bool canMorph(const PathData& path);
bool canMorph(const VectorDrawablePath& path);
- static void verbsToPath(SkPath* outPath, const PathData* data);
- static void interpolatePaths(PathData* outPathData, const PathData* from, const PathData* to,
- float fraction);
+
private:
PathData mData;
SkPath mSkPath;
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index d680f99..dc82041 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -291,20 +291,18 @@
return cachedGlyph;
}
-void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char *text,
int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+ render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr,
0, 0, nullptr, positions);
}
-void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, const SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == nullptr || len == 0) {
+void Font::render(const SkPaint* paint, const char *text, int numGlyphs,
+ const SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == nullptr) {
return;
}
- text += start;
-
int glyphsCount = 0;
SkFixed prevRsbDelta = 0;
@@ -317,7 +315,7 @@
float pathLength = SkScalarToFloat(measure.getLength());
if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2));
float pathOffset = pathLength;
if (paint->getTextAlign() == SkPaint::kCenter_Align) {
textWidth *= 0.5f;
@@ -347,14 +345,14 @@
}
}
-void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::measure(const SkPaint* paint, const char* text,
int numGlyphs, Rect *bounds, const float* positions) {
if (bounds == nullptr) {
ALOGE("No return rectangle provided to measure text");
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
+ render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
}
void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
@@ -378,10 +376,10 @@
}
}
-void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(const SkPaint* paint, const char* text,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == nullptr || len == 0) {
+ if (numGlyphs == 0 || text == nullptr) {
return;
}
@@ -395,7 +393,6 @@
};
RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
- text += start;
int glyphsCount = 0;
while (glyphsCount < numGlyphs) {
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 3119d73..59518a1 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -82,10 +82,10 @@
~Font();
- void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ void render(const SkPaint* paint, const char* text,
int numGlyphs, int x, int y, const float* positions);
- void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ void render(const SkPaint* paint, const char* text,
int numGlyphs, const SkPath* path, float hOffset, float vOffset);
const Font::FontDescription& getDescription() const {
@@ -113,11 +113,11 @@
void precache(const SkPaint* paint, const char* text, int numGlyphs);
- void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ void render(const SkPaint* paint, const char *text,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
- void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ void measure(const SkPaint* paint, const char* text,
int numGlyphs, Rect *bounds, const float* positions);
void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
diff --git a/libs/hwui/microbench/DisplayListCanvasBench.cpp b/libs/hwui/microbench/DisplayListCanvasBench.cpp
index 7a62037..4be1f99 100644
--- a/libs/hwui/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/microbench/DisplayListCanvasBench.cpp
@@ -23,7 +23,7 @@
#include "DisplayListCanvas.h"
#endif
#include "microbench/MicroBench.h"
-#include "unit_tests/TestUtils.h"
+#include "utils/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index 7b8d0e5..eea0c7f 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -21,7 +21,7 @@
#include "OpReorderer.h"
#include "RecordedOp.h"
#include "RecordingCanvas.h"
-#include "unit_tests/TestUtils.h"
+#include "utils/TestUtils.h"
#include "microbench/MicroBench.h"
#include <vector>
@@ -48,7 +48,7 @@
void BM_OpReorderer_defer::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
MicroBench::DoNotOptimize(&reorderer);
}
StopBenchmarkTiming();
@@ -59,11 +59,13 @@
TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) {
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
+ BakedOpRenderer::LightInfo lightInfo = { 50.0f, 128, 128 };
+
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 });
- BakedOpRenderer renderer(caches, renderState, true);
+ BakedOpRenderer renderer(caches, renderState, true, lightInfo);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
diff --git a/libs/hwui/microbench/PathParserBench.cpp b/libs/hwui/microbench/PathParserBench.cpp
index 198035e..3d9fafa 100644
--- a/libs/hwui/microbench/PathParserBench.cpp
+++ b/libs/hwui/microbench/PathParserBench.cpp
@@ -17,20 +17,35 @@
#include <benchmark/Benchmark.h>
#include "PathParser.h"
+#include "VectorDrawablePath.h"
#include <SkPath.h>
using namespace android;
using namespace android::uirenderer;
-BENCHMARK_NO_ARG(BM_PathParser_parseStringPath);
-void BM_PathParser_parseStringPath::Run(int iter) {
- const char* pathString = "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10";
+static const char* sPathString = "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10";
+
+BENCHMARK_NO_ARG(BM_PathParser_parseStringPathForSkPath);
+void BM_PathParser_parseStringPathForSkPath::Run(int iter) {
SkPath skPath;
- size_t length = strlen(pathString);
+ size_t length = strlen(sPathString);
+ PathParser::ParseResult result;
StartBenchmarkTiming();
for (int i = 0; i < iter; i++) {
- PathParser::parseStringForSkPath(&skPath, pathString, length);
+ PathParser::parseStringForSkPath(&skPath, &result, sPathString, length);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_PathParser_parseStringPathForPathData);
+void BM_PathParser_parseStringPathForPathData::Run(int iter) {
+ size_t length = strlen(sPathString);
+ PathData outData;
+ PathParser::ParseResult result;
+ StartBenchmarkTiming();
+ for (int i = 0; i < iter; i++) {
+ PathParser::getPathDataFromString(&outData, &result, sPathString, length);
}
StopBenchmarkTiming();
}
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
new file mode 100644
index 0000000..6b44557
--- /dev/null
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OffscreenBufferPool.h"
+
+#include "Caches.h"
+#include "Properties.h"
+#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
+
+#include <utils/Log.h>
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+////////////////////////////////////////////////////////////////////////////////
+// OffscreenBuffer
+////////////////////////////////////////////////////////////////////////////////
+
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t viewportWidth, uint32_t viewportHeight)
+ : renderState(renderState)
+ , viewportWidth(viewportWidth)
+ , viewportHeight(viewportHeight)
+ , texture(caches) {
+ texture.width = computeIdealDimension(viewportWidth);
+ texture.height = computeIdealDimension(viewportHeight);
+ texture.blend = true;
+
+ caches.textureState().activateTexture(0);
+ glGenTextures(1, &texture.id);
+ caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
+
+ texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+ // not setting filter on texture, since it's set when drawing, based on transform
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+}
+
+void OffscreenBuffer::updateMeshFromRegion() {
+ // avoid T-junctions as they cause artifacts in between the resultant
+ // geometry when complex transforms occur.
+ // TODO: generate the safeRegion only if necessary based on drawing transform
+ Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+ size_t count;
+ const android::Rect* rects = safeRegion.getArray(&count);
+
+ const float texX = 1.0f / float(texture.width);
+ const float texY = 1.0f / float(texture.height);
+
+ FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+ TextureVertex* mesh = &meshVector[0];
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (viewportHeight - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (viewportHeight - r->bottom) * texY;
+
+ TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+ TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+ TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+ TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+ }
+ elementCount = count * 6;
+ renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+ sizeof(TextureVertex) * count * 4,
+ &meshVector[0],
+ GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+uint32_t OffscreenBuffer::computeIdealDimension(uint32_t dimension) {
+ return uint32_t(ceilf(dimension / float(LAYER_SIZE)) * LAYER_SIZE);
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+ texture.deleteTexture();
+ renderState.meshState().deleteMeshBuffer(vbo);
+ elementCount = 0;
+ vbo = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OffscreenBufferPool
+///////////////////////////////////////////////////////////////////////////////
+
+OffscreenBufferPool::OffscreenBufferPool()
+ : mMaxSize(Properties::layerPoolSize) {
+}
+
+OffscreenBufferPool::~OffscreenBufferPool() {
+ clear(); // TODO: unique_ptr?
+}
+
+int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
+ int deltaInt = int(lhs.width) - int(rhs.width);
+ if (deltaInt != 0) return deltaInt;
+
+ return int(lhs.height) - int(rhs.height);
+}
+
+void OffscreenBufferPool::clear() {
+ for (auto entry : mPool) {
+ delete entry.layer;
+ }
+ mPool.clear();
+ mSize = 0;
+}
+
+OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState,
+ const uint32_t width, const uint32_t height) {
+ OffscreenBuffer* layer = nullptr;
+
+ Entry entry(width, height);
+ auto iter = mPool.find(entry);
+
+ if (iter != mPool.end()) {
+ entry = *iter;
+ mPool.erase(iter);
+
+ layer = entry.layer;
+ layer->viewportWidth = width;
+ layer->viewportHeight = height;
+ mSize -= layer->getSizeInBytes();
+ } else {
+ layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height);
+ }
+
+ return layer;
+}
+
+OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
+ const uint32_t width, const uint32_t height) {
+ RenderState& renderState = layer->renderState;
+ if (layer->texture.width == OffscreenBuffer::computeIdealDimension(width)
+ && layer->texture.height == OffscreenBuffer::computeIdealDimension(height)) {
+ // resize in place
+ layer->viewportWidth = width;
+ layer->viewportHeight = height;
+ return layer;
+ }
+ putOrDelete(layer);
+ return get(renderState, width, height);
+}
+
+void OffscreenBufferPool::dump() {
+ for (auto entry : mPool) {
+ ALOGD(" Layer size %dx%d", entry.width, entry.height);
+ }
+}
+
+void OffscreenBufferPool::putOrDelete(OffscreenBuffer* layer) {
+ const uint32_t size = layer->getSizeInBytes();
+ // Don't even try to cache a layer that's bigger than the cache
+ if (size < mMaxSize) {
+ // TODO: Use an LRU
+ while (mSize + size > mMaxSize) {
+ OffscreenBuffer* victim = mPool.begin()->layer;
+ mSize -= victim->getSizeInBytes();
+ delete victim;
+ mPool.erase(mPool.begin());
+ }
+
+ // clear region, since it's no longer valid
+ layer->region.clear();
+
+ Entry entry(layer);
+
+ mPool.insert(entry);
+ mSize += size;
+ } else {
+ delete layer;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
new file mode 100644
index 0000000..fac6c35
--- /dev/null
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
+#define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
+
+#include "Caches.h"
+#include "Texture.h"
+#include "utils/Macros.h"
+
+#include <ui/Region.h>
+
+#include <set>
+
+namespace android {
+namespace uirenderer {
+
+class RenderState;
+
+/**
+ * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
+ * encompasses enough information to draw it back on screen (minus paint properties, which are held
+ * by LayerOp).
+ *
+ * Has two distinct sizes - viewportWidth/viewportHeight describe content area,
+ * texture.width/.height are actual allocated texture size. Texture will tend to be larger than the
+ * viewport bounds, since textures are always allocated with width / height as a multiple of 64, for
+ * the purpose of improving reuse.
+ */
+class OffscreenBuffer {
+public:
+ OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t viewportWidth, uint32_t viewportHeight);
+ ~OffscreenBuffer();
+
+ // must be called prior to rendering, to construct/update vertex buffer
+ void updateMeshFromRegion();
+
+ // Set by RenderNode for HW layers, TODO for clipped saveLayers
+ void setWindowTransform(const Matrix4& transform) {
+ inverseTransformInWindow.loadInverse(transform);
+ }
+
+ static uint32_t computeIdealDimension(uint32_t dimension);
+
+ uint32_t getSizeInBytes() { return texture.width * texture.height * 4; }
+
+ RenderState& renderState;
+
+ uint32_t viewportWidth;
+ uint32_t viewportHeight;
+ Texture texture;
+
+ // Portion of layer that has been drawn to. Used to minimize drawing area when
+ // drawing back to screen / parent FBO.
+ Region region;
+
+ Matrix4 inverseTransformInWindow;
+
+ // vbo / size of mesh
+ GLsizei elementCount = 0;
+ GLuint vbo = 0;
+};
+
+/**
+ * Pool of OffscreenBuffers allocated, but not currently in use.
+ */
+class OffscreenBufferPool {
+public:
+ OffscreenBufferPool();
+ ~OffscreenBufferPool();
+
+ WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState,
+ const uint32_t width, const uint32_t height);
+
+ WARN_UNUSED_RESULT OffscreenBuffer* resize(OffscreenBuffer* layer,
+ const uint32_t width, const uint32_t height);
+
+ void putOrDelete(OffscreenBuffer* layer);
+
+ /**
+ * Clears the pool. This causes all layers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Returns the maximum size of the pool in bytes.
+ */
+ uint32_t getMaxSize() { return mMaxSize; }
+
+ /**
+ * Returns the current size of the pool in bytes.
+ */
+ uint32_t getSize() { return mSize; }
+
+ size_t getCount() { return mPool.size(); }
+
+ /**
+ * Prints out the content of the pool.
+ */
+ void dump();
+private:
+ struct Entry {
+ Entry() {}
+
+ Entry(const uint32_t layerWidth, const uint32_t layerHeight)
+ : width(OffscreenBuffer::computeIdealDimension(layerWidth))
+ , height(OffscreenBuffer::computeIdealDimension(layerHeight)) {}
+
+ Entry(OffscreenBuffer* layer)
+ : layer(layer)
+ , width(layer->texture.width)
+ , height(layer->texture.height) {
+ }
+
+ static int compare(const Entry& lhs, const Entry& rhs);
+
+ bool operator==(const Entry& other) const {
+ return compare(*this, other) == 0;
+ }
+
+ bool operator!=(const Entry& other) const {
+ return compare(*this, other) != 0;
+ }
+
+ bool operator<(const Entry& other) const {
+ return Entry::compare(*this, other) < 0;
+ }
+
+ OffscreenBuffer* layer = nullptr;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ }; // struct Entry
+
+ std::multiset<Entry> mPool;
+
+ uint32_t mSize = 0;
+ uint32_t mMaxSize;
+}; // class OffscreenBufferCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 9637117..4fa8200 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -90,6 +90,8 @@
}
*/
+ mLayerPool.clear();
+
// TODO: reset all cached state in state objects
std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
mAssetAtlas.terminate();
@@ -106,6 +108,19 @@
mStencil = nullptr;
}
+void RenderState::flush(Caches::FlushMode mode) {
+ switch (mode) {
+ case Caches::FlushMode::Full:
+ // fall through
+ case Caches::FlushMode::Moderate:
+ // fall through
+ case Caches::FlushMode::Layers:
+ mLayerPool.clear();
+ break;
+ }
+ mCaches->flush(mode);
+}
+
void RenderState::setViewport(GLsizei width, GLsizei height) {
mViewportWidth = width;
mViewportHeight = height;
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 3cda170..dcd5ea6 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -21,6 +21,7 @@
#include "Glop.h"
#include "renderstate/Blend.h"
#include "renderstate/MeshState.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/PixelBufferState.h"
#include "renderstate/Scissor.h"
#include "renderstate/Stencil.h"
@@ -56,6 +57,8 @@
void onGLContextCreated();
void onGLContextDestroyed();
+ void flush(Caches::FlushMode flushMode);
+
void setViewport(GLsizei width, GLsizei height);
void getViewport(GLsizei* outWidth, GLsizei* outHeight);
@@ -97,6 +100,8 @@
Scissor& scissor() { return *mScissor; }
Stencil& stencil() { return *mStencil; }
+ OffscreenBufferPool& layerPool() { return mLayerPool; }
+
void dump();
private:
@@ -116,6 +121,8 @@
Scissor* mScissor = nullptr;
Stencil* mStencil = nullptr;
+ OffscreenBufferPool mLayerPool;
+
AssetAtlas mAssetAtlas;
std::set<Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> mRegisteredContexts;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1b89960..ca85dfb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,6 @@
#include "utils/TimeUtils.h"
#if HWUI_NEW_OPS
-#include "BakedOpRenderer.h"
#include "OpReorderer.h"
#endif
@@ -150,13 +149,23 @@
// TODO: don't pass viewport size, it's automatic via EGL
void CanvasContext::setup(int width, int height, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+#if HWUI_NEW_OPS
+ mLightInfo.lightRadius = lightRadius;
+ mLightInfo.ambientShadowAlpha = ambientShadowAlpha;
+ mLightInfo.spotShadowAlpha = spotShadowAlpha;
+#else
if (!mCanvas) return;
mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha);
+#endif
}
void CanvasContext::setLightCenter(const Vector3& lightCenter) {
+#if HWUI_NEW_OPS
+ mLightCenter = lightCenter;
+#else
if (!mCanvas) return;
mCanvas->setLightCenter(lightCenter);
+#endif
}
void CanvasContext::setOpaque(bool opaque) {
@@ -228,7 +237,7 @@
if (CC_LIKELY(mSwapHistory.size())) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
const SwapHistory& lastSwap = mSwapHistory.back();
- int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
+ nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
// The slight fudge-factor is to deal with cases where
// the vsync was estimated due to being slow handling the signal.
// See the logic in TimeLord#computeFrameTimeNanos or in
@@ -340,9 +349,11 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes);
+ OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+ mRenderNodes, mLightCenter);
mLayerUpdateQueue.clear();
- BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
+ BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
+ mOpaque, mLightInfo);
// TODO: profiler().draw(mCanvas);
reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
@@ -576,8 +587,12 @@
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+#else
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
+#endif
node->incStrong(nullptr);
mPrefetechedLayers.insert(node);
@@ -599,7 +614,7 @@
// Make sure to release all the textures we were owning as there won't
// be another draw
caches.textureCache.resetMarkInUse(this);
- caches.flush(Caches::FlushMode::Layers);
+ mRenderThread.renderState().flush(Caches::FlushMode::Layers);
}
}
@@ -609,10 +624,10 @@
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
- Caches::getInstance().flush(Caches::FlushMode::Full);
+ thread.renderState().flush(Caches::FlushMode::Full);
thread.eglManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- Caches::getInstance().flush(Caches::FlushMode::Moderate);
+ thread.renderState().flush(Caches::FlushMode::Moderate);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index d656014..c3cfc94 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -27,6 +27,10 @@
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
+#if HWUI_NEW_OPS
+#include "BakedOpRenderer.h"
+#endif
+
#include <cutils/compiler.h>
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -165,6 +169,11 @@
bool mOpaque;
OpenGLRenderer* mCanvas = nullptr;
+#if HWUI_NEW_OPS
+ BakedOpRenderer::LightInfo mLightInfo;
+ Vector3 mLightCenter = { 0, 0, 0 };
+#endif
+
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
LayerUpdateQueue mLayerUpdateQueue;
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
index e16310e..3f87d7f 100644
--- a/libs/hwui/tests/Benchmark.h
+++ b/libs/hwui/tests/Benchmark.h
@@ -16,6 +16,8 @@
#ifndef TESTS_BENCHMARK_H
#define TESTS_BENCHMARK_H
+#include "TestScene.h"
+
#include <string>
#include <vector>
@@ -26,12 +28,17 @@
int count;
};
-typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+typedef test::TestScene* (*CreateScene)(const BenchmarkOptions&);
+
+template <class T>
+test::TestScene* simpleCreateScene(const BenchmarkOptions&) {
+ return new T();
+}
struct BenchmarkInfo {
std::string name;
std::string description;
- BenchmarkFunctor functor;
+ CreateScene createScene;
};
class Benchmark {
diff --git a/libs/hwui/tests/TestScene.h b/libs/hwui/tests/TestScene.h
new file mode 100644
index 0000000..b5d8954
--- /dev/null
+++ b/libs/hwui/tests/TestScene.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+#ifndef TESTS_TESTSCENE_H
+#define TESTS_TESTSCENE_H
+
+namespace android {
+namespace uirenderer {
+class RenderNode;
+
+#if HWUI_NEW_OPS
+class RecordingCanvas;
+typedef RecordingCanvas TestCanvas;
+#else
+class DisplayListCanvas;
+typedef DisplayListCanvas TestCanvas;
+#endif
+
+namespace test {
+
+class TestScene {
+public:
+ virtual ~TestScene() {}
+ virtual void createContent(int width, int height, TestCanvas& renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+};
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android
+
+#endif /* TESTS_TESTSCENE_H */
diff --git a/libs/hwui/tests/TestSceneRunner.cpp b/libs/hwui/tests/TestSceneRunner.cpp
new file mode 100644
index 0000000..0376e10
--- /dev/null
+++ b/libs/hwui/tests/TestSceneRunner.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnimationContext.h"
+#include "Benchmark.h"
+#include "RenderNode.h"
+#include "TestContext.h"
+#include "scenes/TestSceneBase.h"
+#include "renderthread/RenderProxy.h"
+#include "renderthread/RenderTask.h"
+
+#include <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+void run(const BenchmarkInfo& info, const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ std::unique_ptr<TestScene> scene(info.createScene(opts));
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ sp<RenderNode> rootNode = TestUtils::createNode(0, 0, width, height,
+ [&scene, width, height](RenderProperties& props, TestCanvas& canvas) {
+ props.setClipToBounds(false);
+ scene->createContent(width, height, canvas);
+ });
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false,
+ rootNode.get(), &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < opts.count; i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ scene->doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+}
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
deleted file mode 100644
index 29d9803..0000000
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ /dev/null
@@ -1,428 +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.
- */
-
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RecordingCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-#include <unit_tests/TestUtils.h>
-
-#include "Benchmark.h"
-#include "TestContext.h"
-
-#include "protos/hwui.pb.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <vector>
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-#if HWUI_NEW_OPS
-typedef RecordingCanvas TestCanvas;
-#else
-typedef DisplayListCanvas TestCanvas;
-#endif
-
-
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-
-static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
- TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
- contentCallback(canvas);
- node.setStagingDisplayList(canvas.finishRecording());
-}
-
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(const BenchmarkOptions& opts) {
- // Switch to the real display
- gDisplay = getBuiltInDisplay();
-
- T animation;
- animation.setFrameCount(opts.count);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
- animation.createContent(width, height, &canvas); //TODO: no&
- });
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- canvas->insertReorderBarrier(true);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- canvas->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- canvas->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- recordNode(*node, [](TestCanvas& canvas) {
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- });
- return node;
- }
-};
-static Benchmark _ShadowGrid(BenchmarkInfo{
- "shadowgrid",
- "A grid of rounded rects that cast a shadow. Simplified scenario of an "
- "Android TV-style launcher interface. High CPU/GPU load.",
- TreeContentAnimation::run<ShadowGridAnimation>
-});
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- canvas->insertReorderBarrier(true);
-
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- canvas->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
-
- canvas->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- recordNode(*node, [](TestCanvas& canvas) {
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- });
- return node;
- }
-};
-static Benchmark _ShadowGrid2(BenchmarkInfo{
- "shadowgrid2",
- "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
- "variant of shadowgrid. Very high CPU load, high GPU load.",
- TreeContentAnimation::run<ShadowGrid2Animation>
-});
-
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card = new RenderNode();
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- canvas->insertReorderBarrier(true);
-
- card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- recordNode(*card, [](TestCanvas& canvas) {
- canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < 200; xOffset+=2) {
- for (int yOffset = 0; yOffset < 200; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- canvas.drawRegion(region, paint);
- });
- canvas->drawRenderNode(card.get());
-
- canvas->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-};
-static Benchmark _RectGrid(BenchmarkInfo{
- "rectgrid",
- "A dense grid of 1x1 rects that should visually look like a single rect. "
- "Low CPU/GPU load.",
- TreeContentAnimation::run<RectGridAnimation>
-});
-
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card = new RenderNode();
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- canvas->insertReorderBarrier(true);
-
- card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- recordNode(*card, [](TestCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- canvas.drawOval(0, 0, 200, 200, paint);
- });
- canvas->drawRenderNode(card.get());
-
- canvas->insertReorderBarrier(false);
- }
-
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-};
-static Benchmark _Oval(BenchmarkInfo{
- "oval",
- "Draws 1 oval.",
- TreeContentAnimation::run<OvalAnimation>
-});
-
-class PartialDamageTest : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* canvas) override {
- static SkColor COLORS[] = {
- 0xFFF44336,
- 0xFF9C27B0,
- 0xFF2196F3,
- 0xFF4CAF50,
- };
-
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
- COLORS[static_cast<int>((y / dp(116))) % 4]);
- canvas->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- cards[0]->mutateStagingProperties().setTranslationX(curFrame);
- cards[0]->mutateStagingProperties().setTranslationY(curFrame);
- cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
- canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- });
- }
-
- static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
- int startA = (start >> 24) & 0xff;
- int startR = (start >> 16) & 0xff;
- int startG = (start >> 8) & 0xff;
- int startB = start & 0xff;
-
- int endA = (end >> 24) & 0xff;
- int endR = (end >> 16) & 0xff;
- int endG = (end >> 8) & 0xff;
- int endB = end & 0xff;
-
- return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
- (int)((startR + (int)(fraction * (endR - startR))) << 16) |
- (int)((startG + (int)(fraction * (endG - startG))) << 8) |
- (int)((startB + (int)(fraction * (endB - startB))));
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- recordNode(*node, [color](TestCanvas& canvas) {
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
- });
- return node;
- }
-};
-static Benchmark _PartialDamage(BenchmarkInfo{
- "partialdamage",
- "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
- "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
- "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
- TreeContentAnimation::run<PartialDamageTest>
-});
-
-
-class SaveLayerAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card = new RenderNode();
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
-
- card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- recordNode(*card, [](TestCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
- canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
- canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
- canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
- canvas.restore();
- canvas.restore();
- });
-
- canvas->drawRenderNode(card.get());
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-};
-static Benchmark _SaveLayer(BenchmarkInfo{
- "savelayer",
- "A nested pair of clipped saveLayer operations. "
- "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
- TreeContentAnimation::run<SaveLayerAnimation>
-});
-
-
-class HwLayerAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
- canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
- }, true);
- void createContent(int width, int height, TestCanvas* canvas) override {
- canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
- canvas->drawRenderNode(card.get());
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-};
-static Benchmark _HwLayer(BenchmarkInfo{
- "hwlayer",
- "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
- "Tests the hardware layer codepath.",
- TreeContentAnimation::run<HwLayerAnimation>
-});
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index aee84de..48566e8 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -43,6 +43,8 @@
static int gRepeatCount = 1;
static std::vector<BenchmarkInfo> gRunTests;
+void run(const BenchmarkInfo& info, const BenchmarkOptions& opts);
+
static void printHelp() {
printf("\
USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
@@ -186,7 +188,7 @@
opts.count = gFrameCount;
for (int i = 0; i < gRepeatCount; i++) {
for (auto&& test : gRunTests) {
- test.functor(opts);
+ run(test, opts);
}
}
printf("Success!\n");
diff --git a/libs/hwui/tests/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/scenes/HwLayerAnimation.cpp
new file mode 100644
index 0000000..e316eca
--- /dev/null
+++ b/libs/hwui/tests/scenes/HwLayerAnimation.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class HwLayerAnimation;
+
+static Benchmark _HwLayer(BenchmarkInfo{
+ "hwlayer",
+ "A nested pair of nodes with LAYER_TYPE_HARDWARE set on each. "
+ "Tests the hardware layer codepath.",
+ simpleCreateScene<HwLayerAnimation>
+});
+
+class HwLayerAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ card = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, TestCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+ });
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+ canvas.drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
diff --git a/libs/hwui/tests/scenes/OvalAnimation.cpp b/libs/hwui/tests/scenes/OvalAnimation.cpp
new file mode 100644
index 0000000..919a53d
--- /dev/null
+++ b/libs/hwui/tests/scenes/OvalAnimation.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class OvalAnimation;
+
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ simpleCreateScene<OvalAnimation>
+});
+
+class OvalAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.insertReorderBarrier(true);
+
+ card = TestUtils::createNode(0, 0, 200, 200, [](TestCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ canvas.drawOval(0, 0, 200, 200, paint);
+ });
+
+ canvas.drawRenderNode(card.get());
+ canvas.insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
diff --git a/libs/hwui/tests/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/scenes/PartialDamageAnimation.cpp
new file mode 100644
index 0000000..0fba4eb
--- /dev/null
+++ b/libs/hwui/tests/scenes/PartialDamageAnimation.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class PartialDamageAnimation;
+
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ simpleCreateScene<PartialDamageAnimation>
+});
+
+class PartialDamageAnimation : public TestScene {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ SkColor color = COLORS[static_cast<int>((y / dp(116))) % 4];
+ sp<RenderNode> card = TestUtils::createNode(x, y,
+ x + dp(100), y + dp(100),
+ [color](TestCanvas& canvas) {
+ canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ });
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestUtils::recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
+ SkColor color = TestUtils::interpolateColor(
+ curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0);
+ canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ });
+ }
+};
diff --git a/libs/hwui/tests/scenes/RecentsAnimation.cpp b/libs/hwui/tests/scenes/RecentsAnimation.cpp
new file mode 100644
index 0000000..1e38d84
--- /dev/null
+++ b/libs/hwui/tests/scenes/RecentsAnimation.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class RecentsAnimation;
+
+static Benchmark _Recents(BenchmarkInfo{
+ "recents",
+ "A recents-like scrolling list of textures. "
+ "Consists of updating a texture every frame",
+ simpleCreateScene<RecentsAnimation>
+});
+
+class RecentsAnimation : public TestScene {
+public:
+ void createContent(int width, int height, TestCanvas& renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ thumbnailSize = std::min(std::min(width, height) / 2, 720);
+ int cardsize = std::min(width, height) - dp(64);
+
+ renderer.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer.insertReorderBarrier(true);
+
+ int x = dp(32);
+ for (int i = 0; i < 4; i++) {
+ int y = (height / 4) * i;
+ SkBitmap thumb = TestUtils::createSkBitmap(thumbnailSize, thumbnailSize);
+ thumb.eraseColor(COLORS[i]);
+ sp<RenderNode> card = createCard(x, y, cardsize, cardsize, thumb);
+ card->mutateStagingProperties().setElevation(i * dp(8));
+ renderer.drawRenderNode(card.get());
+ mThumbnail = thumb;
+ mCards.push_back(card);
+ }
+
+ renderer.insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < mCards.size(); ci++) {
+ mCards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ mCards[ci]->setPropertyFieldsDirty(RenderNode::Y);
+ }
+ mThumbnail.eraseColor(TestUtils::interpolateColor(
+ curFrame / 150.0f, 0xFF4CAF50, 0xFFFF5722));
+ }
+
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height,
+ const SkBitmap& thumb) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [&thumb, width, height](RenderProperties& props, TestCanvas& canvas) {
+ props.setElevation(dp(16));
+ props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ props.mutableOutline().setShouldClip(true);
+
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(),
+ 0, 0, width, height, nullptr);
+ });
+ }
+
+ SkBitmap mThumbnail;
+ std::vector< sp<RenderNode> > mCards;
+ int thumbnailSize;
+};
diff --git a/libs/hwui/tests/scenes/RectGridAnimation.cpp b/libs/hwui/tests/scenes/RectGridAnimation.cpp
new file mode 100644
index 0000000..254f828
--- /dev/null
+++ b/libs/hwui/tests/scenes/RectGridAnimation.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "TestSceneBase.h"
+
+class RectGridAnimation;
+
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ simpleCreateScene<RectGridAnimation>
+});
+
+class RectGridAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.insertReorderBarrier(true);
+
+ card = TestUtils::createNode(50, 50, 250, 250,
+ [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < 200; xOffset+=2) {
+ for (int yOffset = 0; yOffset < 200; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ canvas.drawRegion(region, paint);
+ });
+ canvas.drawRenderNode(card.get());
+
+ canvas.insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
diff --git a/libs/hwui/tests/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/scenes/SaveLayerAnimation.cpp
new file mode 100644
index 0000000..c62dd19
--- /dev/null
+++ b/libs/hwui/tests/scenes/SaveLayerAnimation.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class SaveLayerAnimation;
+
+static Benchmark _SaveLayer(BenchmarkInfo{
+ "savelayer",
+ "A nested pair of clipped saveLayer operations. "
+ "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
+ simpleCreateScene<SaveLayerAnimation>
+});
+
+class SaveLayerAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+
+ card = TestUtils::createNode(0, 0, 200, 200,
+ [](TestCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+ canvas.restore();
+ canvas.restore();
+ });
+
+ canvas.drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+};
diff --git a/libs/hwui/tests/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/scenes/ShadowGrid2Animation.cpp
new file mode 100644
index 0000000..26c86aa
--- /dev/null
+++ b/libs/hwui/tests/scenes/ShadowGrid2Animation.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class ShadowGrid2Animation;
+
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ simpleCreateScene<ShadowGrid2Animation>
+});
+
+class ShadowGrid2Animation : public TestScene {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ canvas.insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [width, height](RenderProperties& props, TestCanvas& canvas) {
+ props.setElevation(dp(16));
+ props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ props.mutableOutline().setShouldClip(true);
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
+ }
+};
diff --git a/libs/hwui/tests/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/scenes/ShadowGridAnimation.cpp
new file mode 100644
index 0000000..ee3c590
--- /dev/null
+++ b/libs/hwui/tests/scenes/ShadowGridAnimation.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class ShadowGridAnimation;
+
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ simpleCreateScene<ShadowGridAnimation>
+});
+
+class ShadowGridAnimation : public TestScene {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ canvas.insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [width, height](RenderProperties& props, TestCanvas& canvas) {
+ props.setElevation(dp(16));
+ props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ props.mutableOutline().setShouldClip(true);
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
+ }
+};
diff --git a/libs/hwui/tests/scenes/TestSceneBase.h b/libs/hwui/tests/scenes/TestSceneBase.h
new file mode 100644
index 0000000..a208509
--- /dev/null
+++ b/libs/hwui/tests/scenes/TestSceneBase.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+#ifndef TESTS_SCENES_TESTSCENEBASE_H
+#define TESTS_SCENES_TESTSCENEBASE_H
+
+#include "DisplayListCanvas.h"
+#include "RecordingCanvas.h"
+#include "RenderNode.h"
+#include "tests/Benchmark.h"
+#include "tests/TestContext.h"
+#include "tests/TestScene.h"
+#include "utils/TestUtils.h"
+
+#include <functional>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+#endif /* TESTS_SCENES_TESTSCENEBASE_H_ */
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
index bc1b69f..7ad2f9b 100644
--- a/libs/hwui/unit_tests/BakedOpStateTests.cpp
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -18,7 +18,7 @@
#include <BakedOpState.h>
#include <RecordedOp.h>
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
namespace android {
namespace uirenderer {
@@ -60,24 +60,21 @@
TEST(BakedOpState, constructAndReject) {
LinearAllocator allocator;
- Matrix4 identity;
- identity.loadIdentity();
-
Matrix4 translate100x0;
translate100x0.loadTranslate(100, 0, 0);
SkPaint paint;
{
RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
}
{
- RectOp successOp(Rect(30, 40, 100, 200), identity, Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
@@ -85,5 +82,24 @@
}
}
+TEST(BakedOpState, oplessConstructAndReject) {
+ LinearAllocator allocator;
+ {
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 0, 0)); // empty
+ BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+
+ EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
+ EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ }
+ {
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+
+ EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
+ EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+ EXPECT_EQ((ShadowOp*)0x1234, bakedOp->op);
+ }
+}
+
}
}
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
index 3ef329a..c6ccf4d 100644
--- a/libs/hwui/unit_tests/FatVectorTests.cpp
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -17,7 +17,7 @@
#include <gtest/gtest.h>
#include <utils/FatVector.h>
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
index ef205ec..05fd08a 100644
--- a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp
@@ -19,7 +19,7 @@
#include <LayerUpdateQueue.h>
#include <RenderNode.h>
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
index 0f6b249..0591db6 100644
--- a/libs/hwui/unit_tests/LinearAllocatorTests.cpp
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -17,7 +17,7 @@
#include <gtest/gtest.h>
#include <utils/LinearAllocator.h>
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp b/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp
new file mode 100644
index 0000000..de86aed
--- /dev/null
+++ b/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <renderstate/OffscreenBufferPool.h>
+
+#include <utils/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(OffscreenBuffer, computeIdealDimension) {
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(1));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(31));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(33));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(64));
+ EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
+}
+
+TEST(OffscreenBuffer, construct) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBuffer layer(thread.renderState(), Caches::getInstance(), 49u, 149u);
+ EXPECT_EQ(49u, layer.viewportWidth);
+ EXPECT_EQ(149u, layer.viewportHeight);
+
+ EXPECT_EQ(64u, layer.texture.width);
+ EXPECT_EQ(192u, layer.texture.height);
+
+ EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
+ });
+}
+
+TEST(OffscreenBufferPool, construct) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+ EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
+ EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
+ EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
+ << "pool must read size from Properties";
+ });
+
+}
+
+TEST(OffscreenBufferPool, getPutClear) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(thread.renderState(), 100u, 200u);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
+
+ ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
+
+ pool.putOrDelete(layer);
+ ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+ auto layer2 = pool.get(thread.renderState(), 102u, 202u);
+ EXPECT_EQ(layer, layer2) << "layer should be recycled";
+ ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
+
+ pool.putOrDelete(layer);
+ EXPECT_EQ(1u, pool.getCount());
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
+ });
+}
+
+TEST(OffscreenBufferPool, resize) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(thread.renderState(), 64u, 64u);
+
+ // resize in place
+ ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_EQ(60u, layer->viewportWidth);
+ EXPECT_EQ(55u, layer->viewportHeight);
+ EXPECT_EQ(64u, layer->texture.width);
+ EXPECT_EQ(64u, layer->texture.height);
+
+ // resized to use different object in pool
+ auto layer2 = pool.get(thread.renderState(), 128u, 128u);
+ pool.putOrDelete(layer2);
+ ASSERT_EQ(1u, pool.getCount());
+ ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_EQ(120u, layer2->viewportWidth);
+ EXPECT_EQ(125u, layer2->viewportHeight);
+ EXPECT_EQ(128u, layer2->texture.width);
+ EXPECT_EQ(128u, layer2->texture.height);
+
+ // original allocation now only thing in pool
+ EXPECT_EQ(1u, pool.getCount());
+ EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+ });
+}
+
+TEST(OffscreenBufferPool, putAndDestroy) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+ // layer too big to return to the pool
+ // Note: this relies on the fact that the pool won't reject based on max texture size
+ auto hugeLayer = pool.get(thread.renderState(), pool.getMaxSize() / 64, 64);
+ EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
+ pool.putOrDelete(hugeLayer);
+ EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
+ });
+}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index f67c24a..d76086c 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -17,18 +17,26 @@
#include <gtest/gtest.h>
#include <BakedOpState.h>
+#include <LayerUpdateQueue.h>
#include <OpReorderer.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
-#include <renderthread/CanvasContext.h> // todo: remove
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
#include <unordered_map>
namespace android {
namespace uirenderer {
-LayerUpdateQueue sEmptyLayerUpdateQueue;
+const LayerUpdateQueue sEmptyLayerUpdateQueue;
+const Vector3 sLightCenter = {100, 100, 100};
+
+static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ std::vector<sp<RenderNode>> vec;
+ vec.emplace_back(node);
+ return vec;
+}
/**
* Virtual class implemented by each test to redirect static operation / state transitions to
@@ -38,23 +46,23 @@
* and allows Renderer vs Dispatching behavior to be merged.
*
* onXXXOp methods fail by default - tests should override ops they expect
- * startLayer fails by default - tests should override if expected
+ * startRepaintLayer fails by default - tests should override if expected
* startFrame/endFrame do nothing by default - tests should override to intercept
*/
class TestRendererBase {
public:
virtual ~TestRendererBase() {}
- virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) {
+ virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
ADD_FAILURE() << "Layer creation not expected in this test";
return nullptr;
}
- virtual void startLayer(OffscreenBuffer*) {
+ virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
ADD_FAILURE() << "Layer repaint not expected in this test";
}
virtual void endLayer() {
ADD_FAILURE() << "Layer updates not expected in this test";
}
- virtual void startFrame(uint32_t width, uint32_t height) {}
+ virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
virtual void endFrame() {}
// define virtual defaults for direct
@@ -71,7 +79,7 @@
/**
* Dispatches all static methods to similar formed methods on renderer, which fail by default but
- * are overriden by subclasses per test.
+ * are overridden by subclasses per test.
*/
class TestDispatcher {
public:
@@ -82,40 +90,38 @@
MAP_OPS(DISPATCHER_METHOD);
};
-
class FailRenderer : public TestRendererBase {};
-class SimpleTestRenderer : public TestRendererBase {
-public:
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, width);
- EXPECT_EQ(200u, height);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(3, mIndex++);
- }
-};
TEST(OpReorderer, simple) {
+ class SimpleTestRenderer : public TestRendererBase {
+ public:
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(100u, width);
+ EXPECT_EQ(200u, height);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- OpReorderer reorderer(100, 200, *dl);
-
+ OpReorderer reorderer(100, 200, *dl, sLightCenter);
SimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-
TEST(OpReorderer, simpleRejection) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -123,31 +129,31 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
FailRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-
-static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingTestRenderer : public TestRendererBase {
-public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
- }
-};
TEST(OpReorderer, simpleBatching) {
+ const int LOOPS = 5;
+ class SimpleBatchingTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
// 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);
- for (int i = 0; i < SIMPLE_BATCHING_LOOPS; i++) {
+ for (int i = 0; i < LOOPS; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, SkPaint());
canvas.drawBitmap(bitmap, 5, 0, nullptr);
@@ -155,39 +161,67 @@
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
-
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
SimpleBatchingTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
+ EXPECT_EQ(2 * LOOPS, renderer.getIndex())
+ << "Expect number of ops = 2 * loop count"; // TODO: force no merging
}
-class RenderNodeTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- switch(mIndex++) {
- case 0:
- EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- break;
- case 1:
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- break;
- default:
- ADD_FAILURE();
+TEST(OpReorderer, textStrikethroughBatching) {
+ const int LOOPS = 5;
+ class TextStrikethroughTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
}
- }
-};
+ void onTextOp(const TextOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ < LOOPS) << "Text should be beneath all strikethrough rects";
+ }
+ };
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 2000, [](RecordingCanvas& canvas) {
+ SkPaint textPaint;
+ textPaint.setAntiAlias(true);
+ textPaint.setTextSize(20);
+ textPaint.setStrikeThruText(true);
+ for (int i = 0; i < LOOPS; i++) {
+ TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
+ }
+ });
+ OpReorderer reorderer(200, 2000, *dl, sLightCenter);
+ TextStrikethroughTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2 * LOOPS, renderer.getIndex())
+ << "Expect number of ops = 2 * loop count"; // TODO: force no merging
+}
+
TEST(OpReorderer, renderNode) {
- sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ class RenderNodeTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ switch(mIndex++) {
+ case 0:
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ break;
+ case 1:
+ EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
+ sp<RenderNode> child = TestUtils::createNode(10, 10, 110, 110, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
RenderNode* childPtr = child.get();
- sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+ sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
@@ -198,138 +232,128 @@
canvas.restore();
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
-
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
-
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(200, 200), 200, 200, nodes);
-
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
RenderNodeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class ClippedTestRenderer : public TestRendererBase {
-public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
-};
TEST(OpReorderer, clipped) {
- sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
+ class ClippedTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+ };
+
+ sp<RenderNode> node = TestUtils::createNode(0, 0, 200, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
OpReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
- 200, 200, nodes);
-
+ 200, 200, createSyncedNodeList(node), sLightCenter);
ClippedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-
-class SaveLayerSimpleTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(180u, width);
- EXPECT_EQ(180u, height);
- return nullptr;
- }
- void endLayer() override {
- EXPECT_EQ(2, mIndex++);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
- EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
-
- Matrix4 expectedTransform;
- expectedTransform.loadTranslate(-10, -10, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
-};
TEST(OpReorderer, saveLayerSimple) {
+ class SaveLayerSimpleTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(180u, width);
+ EXPECT_EQ(180u, height);
+ return nullptr;
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
+
+ Matrix4 expectedTransform;
+ expectedTransform.loadTranslate(-10, -10, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
-
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
SaveLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-
-/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
- * - createLayer2, rect2 endLayer2
- * - createLayer1, rect1, drawLayer2, endLayer1
- * - startFrame, layerOp1, endFrame
- */
-class SaveLayerNestedTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
- const int index = mIndex++;
- if (index == 0) {
- EXPECT_EQ(400u, width);
- EXPECT_EQ(400u, height);
- return (OffscreenBuffer*) 0x400;
- } else if (index == 3) {
- EXPECT_EQ(800u, width);
- EXPECT_EQ(800u, height);
- return (OffscreenBuffer*) 0x800;
- } else { ADD_FAILURE(); }
- return (OffscreenBuffer*) nullptr;
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 6);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(7, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(9, mIndex++);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 1) {
- EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
- } else if (index == 4) {
- EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
- } else { ADD_FAILURE(); }
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 5) {
- EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
- EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
- EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
- } else { ADD_FAILURE(); }
- }
-};
TEST(OpReorderer, saveLayerNested) {
+ /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
+ * - startTemporaryLayer2, rect2 endLayer2
+ * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
+ * - startFrame, layerOp1, endFrame
+ */
+ class SaveLayerNestedTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ const int index = mIndex++;
+ if (index == 0) {
+ EXPECT_EQ(400u, width);
+ EXPECT_EQ(400u, height);
+ return (OffscreenBuffer*) 0x400;
+ } else if (index == 3) {
+ EXPECT_EQ(800u, width);
+ EXPECT_EQ(800u, height);
+ return (OffscreenBuffer*) 0x800;
+ } else { ADD_FAILURE(); }
+ return (OffscreenBuffer*) nullptr;
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 6);
+ }
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ EXPECT_EQ(7, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(9, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 1) {
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
+ } else if (index == 4) {
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+ } else { ADD_FAILURE(); }
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 5) {
+ EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
+ } else { ADD_FAILURE(); }
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
{
@@ -343,8 +367,7 @@
canvas.restore();
});
- OpReorderer reorderer(800, 800, *dl);
-
+ OpReorderer reorderer(800, 800, *dl, sLightCenter);
SaveLayerNestedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
@@ -362,139 +385,148 @@
canvas.restore();
canvas.restore();
});
- OpReorderer reorderer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl, sLightCenter);
FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class HwLayerSimpleTestRenderer : public TestRendererBase {
-public:
- void startLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
+RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) {
+ class HwLayerSimpleTestRenderer : public TestRendererBase {
+ public:
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
+ EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
+ EXPECT_TRUE(state.computedState.transform.isIdentity())
+ << "Transform should be reset within layer";
- EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override {
- EXPECT_EQ(2, mIndex++);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(3, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(5, mIndex++);
- }
-};
-TEST(OpReorderer, hwLayerSimple) {
- sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
+ << "Damage rect should be used to clip layer content";
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(5, mIndex++);
+ }
+ };
+
+ sp<RenderNode> node = TestUtils::createNode(10, 10, 110, 110,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
- node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- node->setPropertyFieldsDirty(RenderNode::GENERIC);
- OffscreenBuffer** bufferHandle = node->getLayerHandle();
- *bufferHandle = (OffscreenBuffer*) 0x0124;
+ OffscreenBuffer** layerHandle = node->getLayerHandle();
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ // create RenderNode's layer here in same way prepareTree would
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ *layerHandle = &layer;
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
+ auto syncedNodeList = createSyncedNodeList(node);
// only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue;
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
-
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedNodeList, sLightCenter);
HwLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
- *bufferHandle = nullptr;
+ *layerHandle = nullptr;
}
+RENDERTHREAD_TEST(OpReorderer, hwLayerComplex) {
+ /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
+ * - startRepaintLayer(child), rect(grey), endLayer
+ * - startTemporaryLayer, drawLayer(child), endLayer
+ * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
+ * - startFrame, drawLayer(parent), endLayerb
+ */
+ class HwLayerComplexTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
+ EXPECT_EQ(3, mIndex++); // savelayer first
+ return (OffscreenBuffer*)0xabcd;
+ }
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
+ int index = mIndex++;
+ if (index == 0) {
+ // starting inner layer
+ EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
+ } else if (index == 6) {
+ // starting outer layer
+ EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
+ } else { ADD_FAILURE(); }
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 1) {
+ // inner layer's rect (white)
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ } else if (index == 7) {
+ // outer layer's rect (grey)
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ } else { ADD_FAILURE(); }
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 5 || index == 9);
+ }
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ EXPECT_EQ(10, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ OffscreenBuffer* layer = *op.layerHandle;
+ int index = mIndex++;
+ if (index == 4) {
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(100u, layer->viewportHeight);
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
+ } else if (index == 11) {
+ EXPECT_EQ(200u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
+ } else { ADD_FAILURE(); }
+ }
+ void endFrame() override {
+ EXPECT_EQ(12, mIndex++);
+ }
+ };
-/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
- * - startLayer(child), rect(grey), endLayer
- * - createLayer, drawLayer(child), endLayer
- * - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
- * - startFrame, drawLayer(parent), endLayerb
- */
-class HwLayerComplexTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(3, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void startLayer(OffscreenBuffer* offscreenBuffer) override {
- int index = mIndex++;
- if (index == 0) {
- // starting inner layer
- EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
- } else if (index == 6) {
- // starting outer layer
- EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
- } else { ADD_FAILURE(); }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 1) {
- // inner layer's rect (white)
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- } else if (index == 7) {
- // outer layer's rect (grey)
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- } else { ADD_FAILURE(); }
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 5 || index == 9);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(10, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 4) {
- EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- } else if (index == 11) {
- EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
- } else { ADD_FAILURE(); }
- }
- void endFrame() override {
- EXPECT_EQ(12, mIndex++);
- }
-};
-TEST(OpReorderer, hwLayerComplex) {
- auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
- [](RecordingCanvas& canvas) {
+ auto child = TestUtils::createNode(50, 50, 150, 150,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
- child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- child->setPropertyFieldsDirty(RenderNode::GENERIC);
- *(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
+ OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ *(child->getLayerHandle()) = &childLayer;
RenderNode* childPtr = child.get();
- auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
- [childPtr](RecordingCanvas& canvas) {
+ auto parent = TestUtils::createNode(0, 0, 200, 200,
+ [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(0, 0, 200, 200, paint);
@@ -503,21 +535,17 @@
canvas.drawRenderNode(childPtr);
canvas.restore();
});
- parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- parent->setPropertyFieldsDirty(RenderNode::GENERIC);
- *(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
+ OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
+ *(parent->getLayerHandle()) = &parentLayer;
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+ auto syncedList = createSyncedNodeList(parent);
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
-
- LayerUpdateQueue layerUpdateQueue;
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
-
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedList, sLightCenter);
HwLayerComplexTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(13, renderer.getIndex());
@@ -527,21 +555,13 @@
*(parent->getLayerHandle()) = nullptr;
}
-
-class ZReorderTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
- EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
- }
-};
static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
canvas->drawRect(0, 0, 100, 100, paint);
}
static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ auto node = TestUtils::createNode(0, 0, 100, 100,
[expectedDrawOrder](RecordingCanvas& canvas) {
drawOrderedRect(&canvas, expectedDrawOrder);
});
@@ -550,7 +570,15 @@
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
TEST(OpReorderer, zReorder) {
- auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ class ZReorderTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+ }
+ };
+
+ auto parent = TestUtils::createNode(0, 0, 100, 100,
[](RecordingCanvas& canvas) {
drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
drawOrderedRect(&canvas, 1);
@@ -565,46 +593,199 @@
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
-
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(parent.get());
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, nodes);
-
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ createSyncedNodeList(parent), sLightCenter);
ZReorderTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
};
-
-class PropertyTestRenderer : public TestRendererBase {
-public:
- PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
- : mCallback(callback) {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0);
- mCallback(op, state);
- }
- std::function<void(const RectOp&, const BakedOpState&)> mCallback;
-};
-
-static void testProperty(
- std::function<int(RenderProperties&)> propSetupCallback,
- std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) {
+// creates a 100x100 shadow casting node with provided translationZ
+static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
+ return TestUtils::createNode(0, 0, 100, 100,
+ [translationZ] (RenderProperties& properties, RecordingCanvas& canvas) {
+ properties.setTranslationZ(translationZ);
+ properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
- node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+}
- std::vector< sp<RenderNode> > nodes;
- nodes.push_back(node.get());
+TEST(OpReorderer, shadow) {
+ class ShadowTestRenderer : public TestRendererBase {
+ public:
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
+ EXPECT_TRUE(op.casterPath->isRect(nullptr));
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowMatrixXY);
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(100, 100), 200, 200, nodes);
+ Matrix4 expectedZ;
+ expectedZ.loadTranslate(0, 0, 5);
+ EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowMatrixZ);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ };
+ sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
+ [] (RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
+ ShadowTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
+TEST(OpReorderer, shadowSaveLayer) {
+ class ShadowSaveLayerTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ return nullptr;
+ }
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_FLOAT_EQ(50, op.lightCenter.x);
+ EXPECT_FLOAT_EQ(40, op.lightCenter.y);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endLayer() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ };
+
+ sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
+ [] (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);
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.insertReorderBarrier(false);
+ canvas.restoreToCount(count);
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
+ ShadowSaveLayerTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(5, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
+ class ShadowHwLayerTestRenderer : public TestRendererBase {
+ public:
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_FLOAT_EQ(50, op.lightCenter.x);
+ EXPECT_FLOAT_EQ(40, op.lightCenter.y);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endLayer() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ };
+
+ sp<RenderNode> parent = TestUtils::createNode(50, 60, 150, 160,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
+ canvas.insertReorderBarrier(true);
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(20, 10);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.restore();
+ });
+ OffscreenBuffer** layerHandle = parent->getLayerHandle();
+
+ // create RenderNode's layer here in same way prepareTree would, setting windowTransform
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ Matrix4 windowTransform;
+ windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
+ layer.setWindowTransform(windowTransform);
+ *layerHandle = &layer;
+
+ auto syncedList = createSyncedNodeList(parent);
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
+ layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
+ OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedList, (Vector3) { 100, 100, 100 });
+ ShadowHwLayerTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(5, renderer.getIndex());
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ *layerHandle = nullptr;
+}
+
+TEST(OpReorderer, shadowLayering) {
+ class ShadowLayeringTestRenderer : public TestRendererBase {
+ public:
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 0 || index == 1);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 3);
+ }
+ };
+ sp<RenderNode> parent = TestUtils::createNode(0, 0, 200, 200,
+ [] (RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
+ canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ createSyncedNodeList(parent), sLightCenter);
+ ShadowLayeringTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
+ std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
+ class PropertyTestRenderer : public TestRendererBase {
+ public:
+ PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
+ : mCallback(callback) {}
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0);
+ mCallback(op, state);
+ }
+ std::function<void(const RectOp&, const BakedOpState&)> mCallback;
+ };
+
+ auto node = TestUtils::createNode(0, 0, 100, 100,
+ [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
+ propSetupCallback(props);
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ createSyncedNodeList(node), sLightCenter);
PropertyTestRenderer renderer(opValidateCallback);
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
@@ -614,7 +795,6 @@
testProperty([](RenderProperties& properties) {
properties.setAlpha(0.5f);
properties.setHasOverlappingRendering(false);
- return RenderNode::ALPHA | RenderNode::GENERIC;
}, [](const RectOp& op, const BakedOpState& state) {
EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
});
@@ -624,7 +804,6 @@
testProperty([](RenderProperties& properties) {
properties.setClipToBounds(true);
properties.setClipBounds(Rect(10, 20, 300, 400));
- return RenderNode::GENERIC;
}, [](const RectOp& op, const BakedOpState& state) {
EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
<< "Clip rect should be intersection of node bounds and clip bounds";
@@ -634,7 +813,6 @@
TEST(OpReorderer, renderPropRevealClip) {
testProperty([](RenderProperties& properties) {
properties.mutableRevealClip().set(true, 50, 50, 25);
- return RenderNode::GENERIC;
}, [](const RectOp& op, const BakedOpState& state) {
ASSERT_NE(nullptr, state.roundRectClipState);
EXPECT_TRUE(state.roundRectClipState->highPriority);
@@ -647,7 +825,6 @@
testProperty([](RenderProperties& properties) {
properties.mutableOutline().setShouldClip(true);
properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
- return RenderNode::GENERIC;
}, [](const RectOp& op, const BakedOpState& state) {
ASSERT_NE(nullptr, state.roundRectClipState);
EXPECT_FALSE(state.roundRectClipState->highPriority);
@@ -671,9 +848,6 @@
properties.setTranslationY(20);
properties.setScaleX(0.5f);
properties.setScaleY(0.7f);
- return RenderNode::GENERIC
- | RenderNode::TRANSLATION_X | RenderNode::TRANSLATION_Y
- | RenderNode::SCALE_X | RenderNode::SCALE_Y;
}, [](const RectOp& op, const BakedOpState& state) {
Matrix4 matrix;
matrix.loadTranslate(10, 10, 0); // left, top
@@ -692,5 +866,122 @@
});
}
+struct SaveLayerAlphaData {
+ uint32_t layerWidth = 0;
+ uint32_t layerHeight = 0;
+ Rect rectClippedBounds;
+ Matrix4 rectMatrix;
+};
+/**
+ * Constructs a view to hit the temporary layer alpha property implementation:
+ * a) 0 < alpha < 1
+ * b) too big for layer (larger than maxTextureSize)
+ * c) overlapping rendering content
+ * returning observed data about layer size and content clip/transform.
+ *
+ * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
+ * (for efficiency, and to fit in layer size constraints) based on parent clip.
+ */
+void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
+ std::function<void(RenderProperties&)> propSetupCallback) {
+ class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
+ public:
+ SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
+ : mOutData(outData) {}
+
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ mOutData->layerWidth = width;
+ mOutData->layerHeight = height;
+ return nullptr;
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+
+ mOutData->rectClippedBounds = state.computedState.clippedBounds;
+ mOutData->rectMatrix = state.computedState.transform;
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ private:
+ SaveLayerAlphaData* mOutData;
+ };
+
+ ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
+ << "Node must be bigger than max texture size to exercise saveLayer codepath";
+ auto node = TestUtils::createNode(0, 0, 10000, 10000,
+ [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
+ properties.setHasOverlappingRendering(true);
+ properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
+ // apply other properties
+ propSetupCallback(properties);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 10000, 10000, paint);
+ });
+ auto nodes = createSyncedNodeList(node); // sync before querying height
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+ SaveLayerAlphaClipTestRenderer renderer(outObservedData);
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+
+ // assert, since output won't be valid if we haven't seen a save layer triggered
+ ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
+}
+
+TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
+ SaveLayerAlphaData observedData;
+ testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
+ properties.setTranslationX(10); // offset rendering content
+ properties.setTranslationY(-2000); // offset rendering content
+ });
+ EXPECT_EQ(190u, observedData.layerWidth);
+ EXPECT_EQ(200u, observedData.layerHeight);
+ EXPECT_EQ(Rect(0, 0, 190, 200), observedData.rectClippedBounds)
+ << "expect content to be clipped to screen area";
+ Matrix4 expected;
+ expected.loadTranslate(0, -2000, 0);
+ EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
+ << "expect content to be translated as part of being clipped";
+}
+
+TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
+ SaveLayerAlphaData observedData;
+ testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
+ // Translate and rotate the view so that the only visible part is the top left corner of
+ // the view. It will form an isoceles right triangle with a long side length of 200 at the
+ // bottom of the viewport.
+ properties.setTranslationX(100);
+ properties.setTranslationY(100);
+ properties.setPivotX(0);
+ properties.setPivotY(0);
+ properties.setRotation(45);
+ });
+ // ceil(sqrt(2) / 2 * 200) = 142
+ EXPECT_EQ(142u, observedData.layerWidth);
+ EXPECT_EQ(142u, observedData.layerHeight);
+ EXPECT_EQ(Rect(0, 0, 142, 142), observedData.rectClippedBounds);
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
+}
+
+TEST(OpReorderer, renderPropSaveLayerAlphaScale) {
+ SaveLayerAlphaData observedData;
+ testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
+ properties.setPivotX(0);
+ properties.setPivotY(0);
+ properties.setScaleX(2);
+ properties.setScaleY(0.5f);
+ });
+ EXPECT_EQ(100u, observedData.layerWidth);
+ EXPECT_EQ(400u, observedData.layerHeight);
+ EXPECT_EQ(Rect(0, 0, 100, 400), observedData.rectClippedBounds);
+ EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/unit_tests/PathParserTests.cpp b/libs/hwui/unit_tests/PathParserTests.cpp
deleted file mode 100644
index 244bd6c..0000000
--- a/libs/hwui/unit_tests/PathParserTests.cpp
+++ /dev/null
@@ -1,172 +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.
- */
-
-#include <gtest/gtest.h>
-
-#include "PathParser.h"
-#include "VectorDrawablePath.h"
-
-#include <functional>
-
-namespace android {
-namespace uirenderer {
-
-struct TestData {
- const char* pathString;
- const PathData pathData;
- const std::function<void(SkPath*)> skPathLamda;
-};
-
-static TestData testData1 {
- // Path
- "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z",
- {
- // Verbs
- {'M', 'l', 'z'},
- // Verb sizes
- {2, 4, 0},
- // Points
- {2, 22, 20, 0, 1, -2000},
- },
- [](SkPath* outPath) {
- outPath->moveTo(2, 22);
- outPath->rLineTo(20, 0);
- outPath->rLineTo(1, -2000);
- outPath->close();
- outPath->moveTo(2, 22);
- }
-};
-
-static TestData testData2 {
- // Path
- "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10",
- {
- // Verbs
- {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A', 'a'},
- // VerbSizes
- {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7},
- // Points
- {1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, }
- },
- [](SkPath* outPath) {
- outPath->moveTo(1.0, 1.0);
- outPath->rMoveTo(2.0, 2.0);
- outPath->rLineTo(3.0, 3.0);
- outPath->lineTo(3.0, 3.0);
- outPath->lineTo(4.0, 3.0);
- outPath->rLineTo(4.0, 0);
- outPath->lineTo(8.0, 5.0);
- outPath->rLineTo(0, 5.0);
- outPath->quadTo(6.0, 6.0, 6.0, 6.0);
- outPath->rQuadTo(6.0, 6.0, 6.0, 6.0);
- outPath->rQuadTo(0.0, 0.0, 7.0, 7.0);
- outPath->quadTo(26.0, 26.0, 7.0, 7.0);
- outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
- outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
- outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
- outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
- outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767, 22.8911717921705, 16.737515350332117, 24.986664170401575);
- outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483, 28.644011882390082, 11.155893964798905, 29.37447073281729);
- outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471, 29.954422532383525, 4.0686829203897235, 28.951642951534332);
- outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696, 26.161860541657013, -0.9516429515343354, 23.931317079610267);
- outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987, 19.178055668693663, -1.37447073281729, 16.844106035201087);
- outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546, 12.523358440585524, 3.0133358295984305, 11.262484649667876);
- outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984, 9.552224962671648, 10.000000000000005, 10.0);
- outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881, 12.928932188134523, 2.9289321881345254);
- outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972, 4.870079381441987E-16, 19.999999999999996, 0.0);
- outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112, 1.0542876468501678, 27.071067811865476, 2.9289321881345227);
- outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0, 9.999999999999998);
- outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112, 27.071067811865476, 17.071067811865476);
- outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0, 20.000000000000004, 20.0);
- }
-};
-
-static TestData testData3 {
- // Path
- "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z",
- {
- // Verbs
- {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'},
- // Verb sizes
- {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
- // Points
- {5.3, 13.2, -0.1, 0, -0.3, 0, -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1, 1.3, -1.9, 2.9, -3.4, 4.9, -4.5, 4.1, -2.2, 9.3, -2.2, 13.4, 0, 1.9, 1.1, 3.6, 2.5, 4.9, 4.4, 0.2, 0.3, 0.1, 0.8, -0.2, 1, -0.3, 0.2, -0.8, 0.1, -1, -0.2, -1.2, -1.7, -2.6, -3, -4.3, -4, -3.7, -2, -8.3, -2, -12, 0, -1.7, 0.9, -3.2, 2.3, -4.3, 4, 5.7, 13.1, 5.5, 13.2, 5.3, 13.2},
- },
- [](SkPath* outPath) {
- outPath->moveTo(5.3, 13.2);
- outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1);
- outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0);
- outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5);
- outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0);
- outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4);
- outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0);
- outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2);
- outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0);
- outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0);
- outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0);
- outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2);
- outPath->close();
- outPath->moveTo(5.3, 13.2);
- }
-};
-
-static TestData testData4 {
- // Path
- "l0.0.0.5.0.0.5-0.5.0.0-.5z",
- {
- // Verbs
- {'l', 'z'},
- // Verb sizes
- {10, 0},
- // Points
- {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5},
- },
- [](SkPath* outPath) {
- outPath->rLineTo(0.0, 0.0);
- outPath->rLineTo(0.5, 0.0);
- outPath->rLineTo(0.0, 0.5);
- outPath->rLineTo(-0.5, 0.0);
- outPath->rLineTo(0.0, -0.5);
- outPath->close();
- outPath->moveTo(0.0, 0.0);
- }
-};
-
-const static TestData testDataSet[] = {testData1, testData2, testData3, testData4};
-
-TEST(PathPaser, parseString) {
-
- for (int i = 0; i < 4; i++) {
- // Test generated path data against the given data.
- PathData pathData;
- TestData testData = testDataSet[i];
- size_t length = strlen(testData.pathString);
- PathParser::getPathDataFromString(&pathData, testData.pathString, length);
- PathParser::dump(pathData);
- EXPECT_EQ(testData.pathData, pathData);
-
- // Test SkPath generated
- SkPath expectedPath;
- testData.skPathLamda(&expectedPath);
- SkPath actualPath;
- VectorDrawablePath::verbsToPath(&actualPath, &pathData);
- EXPECT_EQ(expectedPath, actualPath);
- }
-
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index 83b37ab..c23d47e 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -18,7 +18,7 @@
#include <RecordedOp.h>
#include <RecordingCanvas.h>
-#include <unit_tests/TestUtils.h>
+#include <utils/TestUtils.h>
namespace android {
namespace uirenderer {
@@ -41,21 +41,110 @@
playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
}
-TEST(RecordingCanvas, testSimpleRectRecord) {
+TEST(RecordingCanvas, drawLines) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setStrokeWidth(20);
+ float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
+ canvas.drawLines(&points[0], 7, paint);
+ });
+
+ ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+ auto op = dl->getOps()[0];
+ ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
+ EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
+ << "float count must be rounded down to closest multiple of 4";
+ EXPECT_EQ(Rect(-10, -10, 30, 20), op->unmappedBounds)
+ << "unmapped bounds must be size of line, outset by 1/2 stroke width";
+}
+
+TEST(RecordingCanvas, drawRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(10, 20, 90, 180, SkPaint());
});
+ ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+ auto op = *(dl->getOps()[0]);
+ ASSERT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+ EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+}
+
+TEST(RecordingCanvas, drawText) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(20);
+ TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+ });
+
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
count++;
- ASSERT_EQ(RecordedOpId::RectOp, op.opId);
- ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
- ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+ ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect);
+ EXPECT_TRUE(op.localMatrix.isIdentity());
+ EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
+ << "Op expected to be 25+ pixels wide, 10+ pixels tall";
});
ASSERT_EQ(1, count);
}
+TEST(RecordingCanvas, drawText_strikeThruAndUnderline) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(20);
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ paint.setUnderlineText(i != 0);
+ paint.setStrikeThruText(j != 0);
+ TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+ }
+ }
+ });
+
+ auto ops = dl->getOps();
+ ASSERT_EQ(8u, ops.size());
+
+ int index = 0;
+ EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
+
+ EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+ EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
+
+ EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+ EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
+
+ EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
+ EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
+ EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
+}
+
+TEST(RecordingCanvas, drawText_forceAlignLeft) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(20);
+ paint.setTextAlign(SkPaint::kLeft_Align);
+ TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+ paint.setTextAlign(SkPaint::kCenter_Align);
+ TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+ paint.setTextAlign(SkPaint::kRight_Align);
+ TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
+ });
+
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ count++;
+ ASSERT_EQ(RecordedOpId::TextOp, op.opId);
+ EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
+ << "recorded drawText commands must force kLeft_Align on their paint";
+ EXPECT_EQ(SkPaint::kGlyphID_TextEncoding, op.paint->getTextEncoding()); // verify TestUtils
+ });
+ ASSERT_EQ(3, count);
+}
+
TEST(RecordingCanvas, backgroundAndImage) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap;
@@ -109,7 +198,7 @@
ASSERT_EQ(2, count);
}
-TEST(RecordingCanvas, saveLayerSimple) {
+TEST(RecordingCanvas, saveLayer_simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -143,7 +232,7 @@
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayerViewportCrop) {
+TEST(RecordingCanvas, saveLayer_viewportCrop) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// shouldn't matter, since saveLayer will clip to its bounds
canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
@@ -167,7 +256,7 @@
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayerRotateUnclipped) {
+TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
canvas.translate(100, 100);
@@ -193,7 +282,7 @@
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayerRotateClipped) {
+TEST(RecordingCanvas, saveLayer_rotateClipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
canvas.translate(100, 100);
@@ -224,7 +313,7 @@
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, testReorderBarrier) {
+TEST(RecordingCanvas, insertReorderBarrier) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.insertReorderBarrier(true);
diff --git a/libs/hwui/unit_tests/VectorDrawableTests.cpp b/libs/hwui/unit_tests/VectorDrawableTests.cpp
new file mode 100644
index 0000000..77dd73a
--- /dev/null
+++ b/libs/hwui/unit_tests/VectorDrawableTests.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "PathParser.h"
+#include "utils/MathUtils.h"
+#include "utils/VectorDrawableUtils.h"
+
+#include <functional>
+
+namespace android {
+namespace uirenderer {
+
+struct TestData {
+ const char* pathString;
+ const PathData pathData;
+ const std::function<void(SkPath*)> skPathLamda;
+};
+
+const static TestData sTestDataSet[] = {
+ // TestData with scientific notation -2e3 etc.
+ {
+ // Path
+ "M2.000000,22.000000l20.000000,0.000000 1e0-2e3z",
+ {
+ // Verbs
+ {'M', 'l', 'z'},
+ // Verb sizes
+ {2, 4, 0},
+ // Points
+ {2, 22, 20, 0, 1, -2000},
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(2, 22);
+ outPath->rLineTo(20, 0);
+ outPath->rLineTo(1, -2000);
+ outPath->close();
+ outPath->moveTo(2, 22);
+ }
+ },
+
+ // Comprehensive data, containing all the verbs possible.
+ {
+ // Path
+ "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10",
+ {
+ // Verbs
+ {'M', 'm', 'l', 'L', 'H', 'h', 'V', 'v', 'Q', 'q', 't', 'T', 'C', 'c', 'S', 's', 'A', 'a'},
+ // VerbSizes
+ {2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, 6, 6, 4, 4, 7, 7},
+ // Points
+ {1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, 10.0, 10.0, 0.0, 1.0, 1.0, 10.0, 10.0, }
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(1.0, 1.0);
+ outPath->rMoveTo(2.0, 2.0);
+ outPath->rLineTo(3.0, 3.0);
+ outPath->lineTo(3.0, 3.0);
+ outPath->lineTo(4.0, 3.0);
+ outPath->rLineTo(4.0, 0);
+ outPath->lineTo(8.0, 5.0);
+ outPath->rLineTo(0, 5.0);
+ outPath->quadTo(6.0, 6.0, 6.0, 6.0);
+ outPath->rQuadTo(6.0, 6.0, 6.0, 6.0);
+ outPath->rQuadTo(0.0, 0.0, 7.0, 7.0);
+ outPath->quadTo(26.0, 26.0, 7.0, 7.0);
+ outPath->cubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
+ outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
+ outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
+ outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
+ outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767, 22.8911717921705, 16.737515350332117, 24.986664170401575);
+ outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483, 28.644011882390082, 11.155893964798905, 29.37447073281729);
+ outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471, 29.954422532383525, 4.0686829203897235, 28.951642951534332);
+ outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696, 26.161860541657013, -0.9516429515343354, 23.931317079610267);
+ outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987, 19.178055668693663, -1.37447073281729, 16.844106035201087);
+ outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546, 12.523358440585524, 3.0133358295984305, 11.262484649667876);
+ outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984, 9.552224962671648, 10.000000000000005, 10.0);
+ outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881, 12.928932188134523, 2.9289321881345254);
+ outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972, 4.870079381441987E-16, 19.999999999999996, 0.0);
+ outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112, 1.0542876468501678, 27.071067811865476, 2.9289321881345227);
+ outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0, 9.999999999999998);
+ outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112, 27.071067811865476, 17.071067811865476);
+ outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0, 20.000000000000004, 20.0);
+ }
+ },
+
+ // Random long data
+ {
+ // Path
+ "M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z",
+ {
+ // Verbs
+ {'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'C', 'z'},
+ // Verb sizes
+ {2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
+ // Points
+ {5.3, 13.2, -0.1, 0, -0.3, 0, -0.4, -0.1, -0.3, -0.2, -0.4, -0.7, -0.2, -1, 1.3, -1.9, 2.9, -3.4, 4.9, -4.5, 4.1, -2.2, 9.3, -2.2, 13.4, 0, 1.9, 1.1, 3.6, 2.5, 4.9, 4.4, 0.2, 0.3, 0.1, 0.8, -0.2, 1, -0.3, 0.2, -0.8, 0.1, -1, -0.2, -1.2, -1.7, -2.6, -3, -4.3, -4, -3.7, -2, -8.3, -2, -12, 0, -1.7, 0.9, -3.2, 2.3, -4.3, 4, 5.7, 13.1, 5.5, 13.2, 5.3, 13.2},
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(5.3, 13.2);
+ outPath->rCubicTo(-0.1, 0.0, -0.3, 0.0, -0.4, -0.1);
+ outPath->rCubicTo(-0.3, -0.2, -0.4, -0.7, -0.2, -1.0);
+ outPath->rCubicTo(1.3, -1.9, 2.9, -3.4, 4.9, -4.5);
+ outPath->rCubicTo(4.1, -2.2, 9.3, -2.2, 13.4, 0.0);
+ outPath->rCubicTo(1.9, 1.1, 3.6, 2.5, 4.9, 4.4);
+ outPath->rCubicTo(0.2, 0.3, 0.1, 0.8, -0.2, 1.0);
+ outPath->rCubicTo(-0.3, 0.2, -0.8, 0.1, -1.0, -0.2);
+ outPath->rCubicTo(-1.2, -1.7, -2.6, -3.0, -4.3, -4.0);
+ outPath->rCubicTo(-3.7, -2.0, -8.3, -2.0, -12.0, 0.0);
+ outPath->rCubicTo(-1.7, 0.9, -3.2, 2.3, -4.3, 4.0);
+ outPath->cubicTo(5.7, 13.1, 5.5, 13.2, 5.3, 13.2);
+ outPath->close();
+ outPath->moveTo(5.3, 13.2);
+ }
+ },
+
+ // Extreme case with numbers and decimal points crunched together
+ {
+ // Path
+ "l0.0.0.5.0.0.5-0.5.0.0-.5z",
+ {
+ // Verbs
+ {'l', 'z'},
+ // Verb sizes
+ {10, 0},
+ // Points
+ {0, 0, 0.5, 0, 0, 0.5, -0.5, 0, 0, -0.5},
+ },
+ [](SkPath* outPath) {
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rLineTo(0.5, 0.0);
+ outPath->rLineTo(0.0, 0.5);
+ outPath->rLineTo(-0.5, 0.0);
+ outPath->rLineTo(0.0, -0.5);
+ outPath->close();
+ outPath->moveTo(0.0, 0.0);
+ }
+ },
+
+ // Empty test data
+ {
+ "",
+ {
+ // Verbs
+ {},
+ {},
+ {},
+ },
+ [](SkPath* outPath) {}
+ }
+
+};
+
+struct StringPath {
+ const char* stringPath;
+ bool isValid;
+};
+
+const StringPath sStringPaths[] = {
+ {"3e...3", false},
+ {"L.M.F.A.O", false},
+ {"m 1 1", true},
+ {"z", true},
+ {"1-2e34567", false}
+};
+
+static bool hasSameVerbs(const PathData& from, const PathData& to) {
+ return from.verbs == to.verbs && from.verbSizes == to.verbSizes;
+}
+
+TEST(PathParser, parseStringForData) {
+ for (TestData testData: sTestDataSet) {
+ PathParser::ParseResult result;
+ // Test generated path data against the given data.
+ PathData pathData;
+ size_t length = strlen(testData.pathString);
+ PathParser::getPathDataFromString(&pathData, &result, testData.pathString, length);
+ EXPECT_EQ(testData.pathData, pathData);
+ }
+
+ for (StringPath stringPath : sStringPaths) {
+ PathParser::ParseResult result;
+ PathData pathData;
+ SkPath skPath;
+ PathParser::getPathDataFromString(&pathData, &result,
+ stringPath.stringPath, strlen(stringPath.stringPath));
+ EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
+ }
+}
+
+TEST(VectorDrawableUtils, createSkPathFromPathData) {
+ for (TestData testData: sTestDataSet) {
+ SkPath expectedPath;
+ testData.skPathLamda(&expectedPath);
+ SkPath actualPath;
+ VectorDrawableUtils::verbsToPath(&actualPath, testData.pathData);
+ EXPECT_EQ(expectedPath, actualPath);
+ }
+}
+
+TEST(PathParser, parseStringForSkPath) {
+ for (TestData testData: sTestDataSet) {
+ PathParser::ParseResult result;
+ size_t length = strlen(testData.pathString);
+ // Check the return value as well as the SkPath generated.
+ SkPath actualPath;
+ PathParser::parseStringForSkPath(&actualPath, &result, testData.pathString, length);
+ bool hasValidData = !result.failureOccurred;
+ EXPECT_EQ(hasValidData, testData.pathData.verbs.size() > 0);
+ SkPath expectedPath;
+ testData.skPathLamda(&expectedPath);
+ EXPECT_EQ(expectedPath, actualPath);
+ }
+
+ for (StringPath stringPath : sStringPaths) {
+ PathParser::ParseResult result;
+ SkPath skPath;
+ PathParser::parseStringForSkPath(&skPath, &result, stringPath.stringPath,
+ strlen(stringPath.stringPath));
+ EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
+ }
+}
+
+TEST(VectorDrawableUtils, morphPathData) {
+ for (TestData fromData: sTestDataSet) {
+ for (TestData toData: sTestDataSet) {
+ bool canMorph = VectorDrawableUtils::canMorph(fromData.pathData, toData.pathData);
+ if (fromData.pathData == toData.pathData) {
+ EXPECT_TRUE(canMorph);
+ } else {
+ bool expectedToMorph = hasSameVerbs(fromData.pathData, toData.pathData);
+ EXPECT_EQ(expectedToMorph, canMorph);
+ }
+ }
+ }
+}
+
+TEST(VectorDrawableUtils, interpolatePathData) {
+ // Interpolate path data with itself and every other path data
+ for (TestData fromData: sTestDataSet) {
+ for (TestData toData: sTestDataSet) {
+ PathData outData;
+ bool success = VectorDrawableUtils::interpolatePathData(&outData, fromData.pathData,
+ toData.pathData, 0.5);
+ bool expectedToMorph = hasSameVerbs(fromData.pathData, toData.pathData);
+ EXPECT_EQ(expectedToMorph, success);
+ }
+ }
+
+ float fractions[] = {0, 0.00001, 0.28, 0.5, 0.7777, 0.9999999, 1};
+ // Now try to interpolate with a slightly modified version of self and expect success
+ for (TestData fromData : sTestDataSet) {
+ PathData toPathData = fromData.pathData;
+ for (size_t i = 0; i < toPathData.points.size(); i++) {
+ toPathData.points[i]++;
+ }
+ const PathData& fromPathData = fromData.pathData;
+ PathData outData;
+ // Interpolate the two path data with different fractions
+ for (float fraction : fractions) {
+ bool success = VectorDrawableUtils::interpolatePathData(
+ &outData, fromPathData, toPathData, fraction);
+ EXPECT_TRUE(success);
+ for (size_t i = 0; i < outData.points.size(); i++) {
+ float expectedResult = fromPathData.points[i] * (1.0 - fraction) +
+ toPathData.points[i] * fraction;
+ EXPECT_TRUE(MathUtils::areEqual(expectedResult, outData.points[i]));
+ }
+ }
+ }
+}
+
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 5ca9083..ccf2287 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -35,4 +35,7 @@
static_assert(std::is_standard_layout<Type>::value, \
#Type " must have standard layout")
+#define WARN_UNUSED_RESULT \
+ __attribute__((warn_unused_result))
+
#endif /* MACROS_H */
diff --git a/libs/hwui/utils/TestUtils.cpp b/libs/hwui/utils/TestUtils.cpp
new file mode 100644
index 0000000..dd6fc36
--- /dev/null
+++ b/libs/hwui/utils/TestUtils.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24)
+ | (int)((startR + (int)(fraction * (endR - startR))) << 16)
+ | (int)((startG + (int)(fraction * (endG - startG))) << 8)
+ | (int)((startB + (int)(fraction * (endB - startB))));
+}
+
+void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+ const SkPaint& inPaint, float x, float y) {
+ // copy to force TextEncoding (which JNI layer would have done)
+ SkPaint paint(inPaint);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ SkMatrix identity;
+ identity.setIdentity();
+ SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+ SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &identity);
+
+ float totalAdvance = 0;
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ Rect bounds;
+ while (*text != '\0') {
+ SkUnichar unichar = SkUTF8_NextUnichar(&text);
+ glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
+ autoCache.getCache()->unicharToGlyph(unichar);
+
+ // push glyph and its relative position
+ glyphs.push_back(glyph);
+ positions.push_back(totalAdvance);
+ positions.push_back(0);
+
+ // compute bounds
+ SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
+ Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
+ glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
+ bounds.unionWith(glyphBounds);
+
+ // advance next character
+ SkScalar skWidth;
+ paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
+ totalAdvance += skWidth;
+ }
+ bounds.translate(x, y);
+ canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), paint, x, y,
+ bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/utils/TestUtils.h
similarity index 66%
rename from libs/hwui/unit_tests/TestUtils.h
rename to libs/hwui/utils/TestUtils.h
index 28e0fd8..f9fa242 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/utils/TestUtils.h
@@ -27,8 +27,10 @@
#if HWUI_NEW_OPS
#include <RecordedOp.h>
+#include <RecordingCanvas.h>
#else
#include <DisplayListOp.h>
+#include <DisplayListCanvas.h>
#endif
#include <memory>
@@ -36,6 +38,12 @@
namespace android {
namespace uirenderer {
+#if HWUI_NEW_OPS
+typedef RecordingCanvas TestCanvas;
+#else
+typedef DisplayListCanvas TestCanvas;
+#endif
+
#define EXPECT_MATRIX_APPROX_EQ(a, b) \
EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
@@ -45,6 +53,20 @@
&& MathUtils::areEqual(a.right, b.right) \
&& MathUtils::areEqual(a.bottom, b.bottom));
+/**
+ * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
+ * (for e.g. accessing its RenderState)
+ */
+#define RENDERTHREAD_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_RenderThreadTest { \
+ public: \
+ static void doTheThing(renderthread::RenderThread& renderThread); \
+ }; \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }; \
+ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
class TestUtils {
public:
class SignalingDtor {
@@ -83,7 +105,8 @@
static SkBitmap createSkBitmap(int width, int height) {
SkBitmap bitmap;
- SkImageInfo info = SkImageInfo::MakeUnknown(width, height);
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ kN32_SkColorType, kPremul_SkAlphaType);
bitmap.setInfo(info);
bitmap.allocPixels(info);
return bitmap;
@@ -97,31 +120,48 @@
return std::unique_ptr<DisplayList>(canvas.finishRecording());
}
- static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(RenderProperties& props, TestCanvas& canvas)> setup = nullptr) {
+#if HWUI_NULL_GPU
// if RenderNodes are being sync'd/used, device info will be needed, since
// DeviceInfo::maxTextureSize() affects layer property
DeviceInfo::initialize();
+#endif
sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- if (onLayer) {
- node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ RenderProperties& props = node->mutateStagingProperties();
+ props.setLeftTopRightBottom(left, top, right, bottom);
+ if (setup) {
+ TestCanvas canvas(props.getWidth(), props.getHeight());
+ setup(props, canvas);
+ node->setStagingDisplayList(canvas.finishRecording());
}
+ node->setPropertyFieldsDirty(0xFFFFFFFF);
return node;
}
- template<class CanvasType>
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
- std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
- sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
+ std::function<void(RenderProperties& props)> setup) {
+ return createNode(left, top, right, bottom,
+ [&setup](RenderProperties& props, TestCanvas& canvas) {
+ setup(props);
+ });
+ }
- auto&& props = node->stagingProperties(); // staging, since not sync'd yet
- CanvasType canvas(props.getWidth(), props.getHeight());
- canvasCallback(canvas);
- node->setStagingDisplayList(canvas.finishRecording());
- return node;
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(TestCanvas& canvas)> setup) {
+ return createNode(left, top, right, bottom,
+ [&setup](RenderProperties& props, TestCanvas& canvas) {
+ setup(canvas);
+ });
+ }
+
+ static void recordNode(RenderNode& node,
+ std::function<void(TestCanvas&)> contentCallback) {
+ TestCanvas canvas(node.stagingProperties().getWidth(),
+ node.stagingProperties().getHeight());
+ contentCallback(canvas);
+ node.setStagingDisplayList(canvas.finishRecording());
}
static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
@@ -153,6 +193,12 @@
TestTask task(rtCallback);
renderthread::RenderThread::getInstance().queueAndWait(&task);
}
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
+
+ static void drawTextToCanvas(TestCanvas* canvas, const char* text,
+ const SkPaint& inPaint, float x, float y);
+
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
node->syncProperties();
diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp
new file mode 100644
index 0000000..ca75c59
--- /dev/null
+++ b/libs/hwui/utils/VectorDrawableUtils.cpp
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VectorDrawableUtils.h"
+
+#include "PathParser.h"
+
+#include <math.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+class PathResolver {
+public:
+ float currentX = 0;
+ float currentY = 0;
+ float ctrlPointX = 0;
+ float ctrlPointY = 0;
+ float currentSegmentStartX = 0;
+ float currentSegmentStartY = 0;
+ void addCommand(SkPath* outPath, char previousCmd,
+ char cmd, const std::vector<float>* points, size_t start, size_t end);
+};
+
+bool VectorDrawableUtils::canMorph(const PathData& morphFrom, const PathData& morphTo) {
+ if (morphFrom.verbs.size() != morphTo.verbs.size()) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < morphFrom.verbs.size(); i++) {
+ if (morphFrom.verbs[i] != morphTo.verbs[i]
+ || morphFrom.verbSizes[i] != morphTo.verbSizes[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VectorDrawableUtils::interpolatePathData(PathData* outData, const PathData& morphFrom,
+ const PathData& morphTo, float fraction) {
+ if (!canMorph(morphFrom, morphTo)) {
+ return false;
+ }
+ interpolatePaths(outData, morphFrom, morphTo, fraction);
+ return true;
+}
+
+ /**
+ * Convert an array of PathVerb to Path.
+ */
+void VectorDrawableUtils::verbsToPath(SkPath* outPath, const PathData& data) {
+ PathResolver resolver;
+ char previousCommand = 'm';
+ size_t start = 0;
+ outPath->reset();
+ for (unsigned int i = 0; i < data.verbs.size(); i++) {
+ size_t verbSize = data.verbSizes[i];
+ resolver.addCommand(outPath, previousCommand, data.verbs[i], &data.points, start,
+ start + verbSize);
+ previousCommand = data.verbs[i];
+ start += verbSize;
+ }
+}
+
+/**
+ * The current PathVerb will be interpolated between the
+ * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+ * <code>fraction</code>.
+ *
+ * @param nodeFrom The start value as a PathVerb.
+ * @param nodeTo The end value as a PathVerb
+ * @param fraction The fraction to interpolate.
+ */
+void VectorDrawableUtils::interpolatePaths(PathData* outData,
+ const PathData& from, const PathData& to, float fraction) {
+ outData->points.resize(from.points.size());
+ outData->verbSizes = from.verbSizes;
+ outData->verbs = from.verbs;
+
+ for (size_t i = 0; i < from.points.size(); i++) {
+ outData->points[i] = from.points[i] * (1 - fraction) + to.points[i] * fraction;
+ }
+}
+
+/**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+static void arcToBezier(SkPath* p,
+ double cx,
+ double cy,
+ double a,
+ double b,
+ double e1x,
+ double e1y,
+ double theta,
+ double start,
+ double sweep) {
+ // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+ // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+ // Maximum of 45 degrees per cubic Bezier segment
+ int numSegments = ceil(fabs(sweep * 4 / M_PI));
+
+ double eta1 = start;
+ double cosTheta = cos(theta);
+ double sinTheta = sin(theta);
+ double cosEta1 = cos(eta1);
+ double sinEta1 = sin(eta1);
+ double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+ double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+ double anglePerSegment = sweep / numSegments;
+ for (int i = 0; i < numSegments; i++) {
+ double eta2 = eta1 + anglePerSegment;
+ double sinEta2 = sin(eta2);
+ double cosEta2 = cos(eta2);
+ double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+ double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+ double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+ double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+ double tanDiff2 = tan((eta2 - eta1) / 2);
+ double alpha =
+ sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+ double q1x = e1x + alpha * ep1x;
+ double q1y = e1y + alpha * ep1y;
+ double q2x = e2x - alpha * ep2x;
+ double q2y = e2y - alpha * ep2y;
+
+ p->cubicTo((float) q1x,
+ (float) q1y,
+ (float) q2x,
+ (float) q2y,
+ (float) e2x,
+ (float) e2y);
+ eta1 = eta2;
+ e1x = e2x;
+ e1y = e2y;
+ ep1x = ep2x;
+ ep1y = ep2y;
+ }
+}
+
+inline double toRadians(float theta) { return theta * M_PI / 180;}
+
+static void drawArc(SkPath* p,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ float a,
+ float b,
+ float theta,
+ bool isMoreThanHalf,
+ bool isPositiveArc) {
+
+ /* Convert rotation angle from degrees to radians */
+ double thetaD = toRadians(theta);
+ /* Pre-compute rotation matrix entries */
+ double cosTheta = cos(thetaD);
+ double sinTheta = sin(thetaD);
+ /* Transform (x0, y0) and (x1, y1) into unit space */
+ /* using (inverse) rotation, followed by (inverse) scale */
+ double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+ double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+ double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+ double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+ /* Compute differences and averages */
+ double dx = x0p - x1p;
+ double dy = y0p - y1p;
+ double xm = (x0p + x1p) / 2;
+ double ym = (y0p + y1p) / 2;
+ /* Solve for intersecting unit circles */
+ double dsq = dx * dx + dy * dy;
+ if (dsq == 0.0) {
+ ALOGW("Points are coincident");
+ return; /* Points are coincident */
+ }
+ double disc = 1.0 / dsq - 1.0 / 4.0;
+ if (disc < 0.0) {
+ ALOGW("Points are too far apart %f", dsq);
+ float adjust = (float) (sqrt(dsq) / 1.99999);
+ drawArc(p, x0, y0, x1, y1, a * adjust,
+ b * adjust, theta, isMoreThanHalf, isPositiveArc);
+ return; /* Points are too far apart */
+ }
+ double s = sqrt(disc);
+ double sdx = s * dx;
+ double sdy = s * dy;
+ double cx;
+ double cy;
+ if (isMoreThanHalf == isPositiveArc) {
+ cx = xm - sdy;
+ cy = ym + sdx;
+ } else {
+ cx = xm + sdy;
+ cy = ym - sdx;
+ }
+
+ double eta0 = atan2((y0p - cy), (x0p - cx));
+
+ double eta1 = atan2((y1p - cy), (x1p - cx));
+
+ double sweep = (eta1 - eta0);
+ if (isPositiveArc != (sweep >= 0)) {
+ if (sweep > 0) {
+ sweep -= 2 * M_PI;
+ } else {
+ sweep += 2 * M_PI;
+ }
+ }
+
+ cx *= a;
+ cy *= b;
+ double tcx = cx;
+ cx = cx * cosTheta - cy * sinTheta;
+ cy = tcx * sinTheta + cy * cosTheta;
+
+ arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+}
+
+
+
+// Use the given verb, and points in the range [start, end) to insert a command into the SkPath.
+void PathResolver::addCommand(SkPath* outPath, char previousCmd,
+ char cmd, const std::vector<float>* points, size_t start, size_t end) {
+
+ int incr = 2;
+ float reflectiveCtrlPointX;
+ float reflectiveCtrlPointY;
+
+ switch (cmd) {
+ case 'z':
+ case 'Z':
+ outPath->close();
+ // Path is closed here, but we need to move the pen to the
+ // closed position. So we cache the segment's starting position,
+ // and restore it here.
+ currentX = currentSegmentStartX;
+ currentY = currentSegmentStartY;
+ ctrlPointX = currentSegmentStartX;
+ ctrlPointY = currentSegmentStartY;
+ outPath->moveTo(currentX, currentY);
+ break;
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ incr = 2;
+ break;
+ case 'h':
+ case 'H':
+ case 'v':
+ case 'V':
+ incr = 1;
+ break;
+ case 'c':
+ case 'C':
+ incr = 6;
+ break;
+ case 's':
+ case 'S':
+ case 'q':
+ case 'Q':
+ incr = 4;
+ break;
+ case 'a':
+ case 'A':
+ incr = 7;
+ break;
+ }
+
+ for (unsigned int k = start; k < end; k += incr) {
+ switch (cmd) {
+ case 'm': // moveto - Start a new sub-path (relative)
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ if (k > start) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ outPath->rLineTo(points->at(k + 0), points->at(k + 1));
+ } else {
+ outPath->rMoveTo(points->at(k + 0), points->at(k + 1));
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
+ break;
+ case 'M': // moveto - Start a new sub-path
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ if (k > start) {
+ // According to the spec, if a moveto is followed by multiple
+ // pairs of coordinates, the subsequent pairs are treated as
+ // implicit lineto commands.
+ outPath->lineTo(points->at(k + 0), points->at(k + 1));
+ } else {
+ outPath->moveTo(points->at(k + 0), points->at(k + 1));
+ currentSegmentStartX = currentX;
+ currentSegmentStartY = currentY;
+ }
+ break;
+ case 'l': // lineto - Draw a line from the current point (relative)
+ outPath->rLineTo(points->at(k + 0), points->at(k + 1));
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ break;
+ case 'L': // lineto - Draw a line from the current point
+ outPath->lineTo(points->at(k + 0), points->at(k + 1));
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ break;
+ case 'h': // horizontal lineto - Draws a horizontal line (relative)
+ outPath->rLineTo(points->at(k + 0), 0);
+ currentX += points->at(k + 0);
+ break;
+ case 'H': // horizontal lineto - Draws a horizontal line
+ outPath->lineTo(points->at(k + 0), currentY);
+ currentX = points->at(k + 0);
+ break;
+ case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+ outPath->rLineTo(0, points->at(k + 0));
+ currentY += points->at(k + 0);
+ break;
+ case 'V': // vertical lineto - Draws a vertical line from the current point
+ outPath->lineTo(currentX, points->at(k + 0));
+ currentY = points->at(k + 0);
+ break;
+ case 'c': // curveto - Draws a cubic Bézier curve (relative)
+ outPath->rCubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
+ points->at(k + 4), points->at(k + 5));
+
+ ctrlPointX = currentX + points->at(k + 2);
+ ctrlPointY = currentY + points->at(k + 3);
+ currentX += points->at(k + 4);
+ currentY += points->at(k + 5);
+
+ break;
+ case 'C': // curveto - Draws a cubic Bézier curve
+ outPath->cubicTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3),
+ points->at(k + 4), points->at(k + 5));
+ currentX = points->at(k + 4);
+ currentY = points->at(k + 5);
+ ctrlPointX = points->at(k + 2);
+ ctrlPointY = points->at(k + 3);
+ break;
+ case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ outPath->rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1),
+ points->at(k + 2), points->at(k + 3));
+ ctrlPointX = currentX + points->at(k + 0);
+ ctrlPointY = currentY + points->at(k + 1);
+ currentX += points->at(k + 2);
+ currentY += points->at(k + 3);
+ break;
+ case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'c' || previousCmd == 's'
+ || previousCmd == 'C' || previousCmd == 'S') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ outPath->cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = points->at(k + 0);
+ ctrlPointY = points->at(k + 1);
+ currentX = points->at(k + 2);
+ currentY = points->at(k + 3);
+ break;
+ case 'q': // Draws a quadratic Bézier (relative)
+ outPath->rQuadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = currentX + points->at(k + 0);
+ ctrlPointY = currentY + points->at(k + 1);
+ currentX += points->at(k + 2);
+ currentY += points->at(k + 3);
+ break;
+ case 'Q': // Draws a quadratic Bézier
+ outPath->quadTo(points->at(k + 0), points->at(k + 1), points->at(k + 2), points->at(k + 3));
+ ctrlPointX = points->at(k + 0);
+ ctrlPointY = points->at(k + 1);
+ currentX = points->at(k + 2);
+ currentY = points->at(k + 3);
+ break;
+ case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+ reflectiveCtrlPointX = 0;
+ reflectiveCtrlPointY = 0;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = currentX - ctrlPointX;
+ reflectiveCtrlPointY = currentY - ctrlPointY;
+ }
+ outPath->rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1));
+ ctrlPointX = currentX + reflectiveCtrlPointX;
+ ctrlPointY = currentY + reflectiveCtrlPointY;
+ currentX += points->at(k + 0);
+ currentY += points->at(k + 1);
+ break;
+ case 'T': // Draws a quadratic Bézier curve (reflective control point)
+ reflectiveCtrlPointX = currentX;
+ reflectiveCtrlPointY = currentY;
+ if (previousCmd == 'q' || previousCmd == 't'
+ || previousCmd == 'Q' || previousCmd == 'T') {
+ reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+ reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+ }
+ outPath->quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+ points->at(k + 0), points->at(k + 1));
+ ctrlPointX = reflectiveCtrlPointX;
+ ctrlPointY = reflectiveCtrlPointY;
+ currentX = points->at(k + 0);
+ currentY = points->at(k + 1);
+ break;
+ case 'a': // Draws an elliptical arc
+ // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ drawArc(outPath,
+ currentX,
+ currentY,
+ points->at(k + 5) + currentX,
+ points->at(k + 6) + currentY,
+ points->at(k + 0),
+ points->at(k + 1),
+ points->at(k + 2),
+ points->at(k + 3) != 0,
+ points->at(k + 4) != 0);
+ currentX += points->at(k + 5);
+ currentY += points->at(k + 6);
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ case 'A': // Draws an elliptical arc
+ drawArc(outPath,
+ currentX,
+ currentY,
+ points->at(k + 5),
+ points->at(k + 6),
+ points->at(k + 0),
+ points->at(k + 1),
+ points->at(k + 2),
+ points->at(k + 3) != 0,
+ points->at(k + 4) != 0);
+ currentX = points->at(k + 5);
+ currentY = points->at(k + 6);
+ ctrlPointX = currentX;
+ ctrlPointY = currentY;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unsupported command: %c", cmd);
+ break;
+ }
+ previousCmd = cmd;
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/utils/VectorDrawableUtils.h b/libs/hwui/utils/VectorDrawableUtils.h
new file mode 100644
index 0000000..21c1cdc
--- /dev/null
+++ b/libs/hwui/utils/VectorDrawableUtils.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
+#define ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
+
+#include "VectorDrawablePath.h"
+
+#include <cutils/compiler.h>
+#include "SkPath.h"
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+class VectorDrawableUtils {
+public:
+ ANDROID_API static bool canMorph(const PathData& morphFrom, const PathData& morphTo);
+ ANDROID_API static bool interpolatePathData(PathData* outData, const PathData& morphFrom,
+ const PathData& morphTo, float fraction);
+ ANDROID_API static void verbsToPath(SkPath* outPath, const PathData& data);
+ static void interpolatePaths(PathData* outPathData, const PathData& from, const PathData& to,
+ float fraction);
+};
+} // namespace uirenderer
+} // namespace android
+#endif /* ANDROID_HWUI_VECTORDRAWABLE_UTILS_H*/
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index c9586e4..bd6af78 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -86,6 +86,9 @@
mLocked.pointerIconChanged = false;
mLocked.requestedPointerShape = mPolicy->getDefaultPointerIconId();
+ mLocked.animationFrameIndex = 0;
+ mLocked.lastFrameUpdatedTime = 0;
+
mLocked.buttonState = 0;
loadResources();
@@ -239,7 +242,8 @@
AutoMutex _l(mLock);
if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
}
if (mLocked.presentation != presentation) {
@@ -402,7 +406,7 @@
updatePointerLocked();
}
-void PointerController::updatePointerShape(int iconId) {
+void PointerController::updatePointerShape(int32_t iconId) {
AutoMutex _l(mLock);
if (mLocked.requestedPointerShape != iconId) {
mLocked.requestedPointerShape = iconId;
@@ -462,8 +466,17 @@
void PointerController::doAnimate(nsecs_t timestamp) {
AutoMutex _l(mLock);
- bool keepAnimating = false;
mLocked.animationPending = false;
+
+ bool keepFading = doFadingAnimationLocked(timestamp);
+ bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp);
+ if (keepFading || keepBitmapFlipping) {
+ startAnimationLocked();
+ }
+}
+
+bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) {
+ bool keepAnimating = false;
nsecs_t frameDelay = timestamp - mLocked.animationTime;
// Animate pointer fade.
@@ -501,10 +514,32 @@
}
}
}
+ return keepAnimating;
+}
- if (keepAnimating) {
- startAnimationLocked();
+bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) {
+ std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find(
+ mLocked.requestedPointerShape);
+ if (iter == mLocked.animationResources.end()) {
+ return false;
}
+
+ if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
+ mSpriteController->openTransaction();
+
+ int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
+ mLocked.animationFrameIndex += incr;
+ mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
+ while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
+ mLocked.animationFrameIndex -= iter->second.animationFrames.size();
+ }
+ mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
+
+ mSpriteController->closeTransaction();
+ }
+
+ // Keep animating.
+ return true;
}
void PointerController::doInactivityTimeout() {
@@ -549,9 +584,16 @@
if (mLocked.requestedPointerShape == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
- std::map<int, SpriteIcon>::const_iterator iter =
+ std::map<int32_t, SpriteIcon>::const_iterator iter =
mLocked.additionalMouseResources.find(mLocked.requestedPointerShape);
if (iter != mLocked.additionalMouseResources.end()) {
+ std::map<int32_t, PointerAnimation>::const_iterator anim_iter =
+ mLocked.animationResources.find(mLocked.requestedPointerShape);
+ if (anim_iter != mLocked.animationResources.end()) {
+ mLocked.animationFrameIndex = 0;
+ mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ startAnimationLocked();
+ }
mLocked.pointerSprite->setIcon(iter->second);
} else {
ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerShape);
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 6d840db..b6c01d2 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -20,6 +20,7 @@
#include "SpriteController.h"
#include <map>
+#include <vector>
#include <ui/DisplayInfo.h>
#include <input/Input.h>
@@ -30,8 +31,6 @@
#include <utils/String8.h>
#include <gui/DisplayEventReceiver.h>
-#include <SkBitmap.h>
-
namespace android {
/*
@@ -43,6 +42,11 @@
SpriteIcon spotAnchor;
};
+struct PointerAnimation {
+ std::vector<SpriteIcon> animationFrames;
+ nsecs_t durationPerFrame;
+};
+
/*
* Pointer controller policy interface.
*
@@ -59,7 +63,8 @@
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
- virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) = 0;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
virtual int32_t getDefaultPointerIconId() = 0;
};
@@ -98,7 +103,7 @@
const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
- void updatePointerShape(int iconId);
+ void updatePointerShape(int32_t iconId);
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
@@ -145,6 +150,9 @@
bool animationPending;
nsecs_t animationTime;
+ size_t animationFrameIndex;
+ nsecs_t lastFrameUpdatedTime;
+
int32_t displayWidth;
int32_t displayHeight;
int32_t displayOrientation;
@@ -162,7 +170,8 @@
SpriteIcon pointerIcon;
bool pointerIconChanged;
- std::map<int, SpriteIcon> additionalMouseResources;
+ std::map<int32_t, SpriteIcon> additionalMouseResources;
+ std::map<int32_t, PointerAnimation> animationResources;
int32_t requestedPointerShape;
@@ -178,6 +187,8 @@
void handleMessage(const Message& message);
int handleEvent(int fd, int events, void* data);
void doAnimate(nsecs_t timestamp);
+ bool doFadingAnimationLocked(nsecs_t timestamp);
+ bool doBitmapAnimationLocked(nsecs_t timestamp);
void doInactivityTimeout();
void startAnimationLocked();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 50df556..c658675 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2658,107 +2658,6 @@
rctlr.stopListeningToSessions();
}
- /**
- * @hide
- * Registers a remote control display that will be sent information by remote control clients.
- * Use this method if your IRemoteControlDisplay is not going to display artwork, otherwise
- * use {@link #registerRemoteControlDisplay(IRemoteControlDisplay, int, int)} to pass the
- * artwork size directly, or
- * {@link #remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay, int, int)} later if artwork
- * is not yet needed.
- * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
- * @param rcd the IRemoteControlDisplay
- */
- public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
- // passing a negative value for art work width and height as they are unknown at this stage
- registerRemoteControlDisplay(rcd, /*w*/-1, /*h*/ -1);
- }
-
- /**
- * @hide
- * Registers a remote control display that will be sent information by remote control clients.
- * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
- * @param rcd
- * @param w the maximum width of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- * @param h the maximum height of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- */
- public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.registerRemoteControlDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e);
- }
- }
-
- /**
- * @hide
- * Unregisters a remote control display that was sent information by remote control clients.
- * @param rcd
- */
- public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlDisplay(rcd);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
- }
- }
-
- /**
- * @hide
- * Sets the artwork size a remote control display expects when receiving bitmaps.
- * @param rcd
- * @param w the maximum width of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- * @param h the maximum height of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- */
- public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.remoteControlDisplayUsesBitmapSize(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in remoteControlDisplayUsesBitmapSize " + e);
- }
- }
-
- /**
- * @hide
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. No effect is null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in remoteControlDisplayWantsPlaybackPositionSync " + e);
- }
- }
/**
* @hide
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6bf5721..445ee6f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -17,6 +17,8 @@
package android.media;
import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -128,6 +130,9 @@
// use sLock to serialize the accesses.
private static final Object sLock = new Object();
+ // Pattern to check non zero timestamp
+ private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
+
/**
* Reads Exif tags from the specified JPEG file.
*/
@@ -367,7 +372,8 @@
*/
public long getDateTime() {
String dateTimeString = mAttributes.get(TAG_DATETIME);
- if (dateTimeString == null) return -1;
+ if (dateTimeString == null
+ || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
ParsePosition pos = new ParsePosition(0);
try {
@@ -402,7 +408,9 @@
public long getGpsDateTime() {
String date = mAttributes.get(TAG_GPS_DATESTAMP);
String time = mAttributes.get(TAG_GPS_TIMESTAMP);
- if (date == null || time == null) return -1;
+ if (date == null || time == null
+ || (!sNonZeroTimePattern.matcher(date).matches()
+ && !sNonZeroTimePattern.matcher(time).matches())) return -1;
String dateTimeString = date + ' ' + time;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8aebe11..693a519 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -23,9 +23,6 @@
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteControlDisplay;
-import android.media.IRemoteVolumeObserver;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.Rating;
@@ -47,8 +44,6 @@
void setStreamVolume(int streamType, int index, int flags, String callingPackage);
- oneway void setRemoteStreamVolume(int index);
-
boolean isStreamMute(int streamType);
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
@@ -121,59 +116,6 @@
int getCurrentAudioFocus();
- /**
- * Register an IRemoteControlDisplay.
- * Success of registration is subject to a check on
- * the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission.
- * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
- * at the top of the stack to update the new display with its information.
- * @param rcd the IRemoteControlDisplay to register. No effect if null.
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- boolean registerRemoteControlDisplay(in IRemoteControlDisplay rcd, int w, int h);
-
- /**
- * Like registerRemoteControlDisplay, but with success being subject to a check on
- * the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission, and if it fails,
- * success is subject to listenerComp being one of the ENABLED_NOTIFICATION_LISTENERS
- * components.
- */
- boolean registerRemoteController(in IRemoteControlDisplay rcd, int w, int h,
- in ComponentName listenerComp);
-
- /**
- * Unregister an IRemoteControlDisplay.
- * No effect if the IRemoteControlDisplay hasn't been successfully registered.
- * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
- */
- oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
- /**
- * Update the size of the artwork used by an IRemoteControlDisplay.
- * @param rcd the IRemoteControlDisplay with the new artwork size requirement
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
- /**
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. Not null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
- boolean wantsSync);
-
void startBluetoothSco(IBinder cb, int targetSdkVersion);
void startBluetoothScoVirtualCall(IBinder cb);
void stopBluetoothSco(IBinder cb);
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
deleted file mode 100644
index aa142d6..0000000
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2011 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;
-
-import android.graphics.Bitmap;
-import android.media.IRemoteControlDisplay;
-import android.media.Rating;
-
-/**
- * @hide
- * Interface registered by AudioManager to notify a source of remote control information
- * that information is requested to be displayed on the remote control (through
- * IRemoteControlDisplay).
- * {@see AudioManager#registerRemoteControlClient(RemoteControlClient)}.
- */
-oneway interface IRemoteControlClient
-{
- /**
- * Notifies a remote control client that information for the given generation ID is
- * requested. If the flags contains
- * {@link RemoteControlClient#FLAG_INFORMATION_REQUESTED_ALBUM_ART} then the width and height
- * parameters are valid.
- * @param generationId
- * @param infoFlags
- * FIXME: is infoFlags required? since the RCC pushes info, this might always be called
- * with RC_INFO_ALL
- */
- void onInformationRequested(int generationId, int infoFlags);
-
- /**
- * Notifies a remote control client that information for the given generation ID is
- * requested for the given IRemoteControlDisplay alone.
- * @param rcd the display to which current info should be sent
- */
- void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h);
-
- /**
- * Sets the generation counter of the current client that is displayed on the remote control.
- */
- void setCurrentClientGenerationId(int clientGeneration);
-
- void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
- void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
- void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
- void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
- void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled);
- void seekTo(int clientGeneration, long timeMs);
- void updateMetadata(int clientGeneration, int key, in Rating value);
-}
\ No newline at end of file
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
deleted file mode 100644
index 1609030..0000000
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2011 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;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/**
- * @hide
- * Interface registered through AudioManager of an object that displays information
- * received from a remote control client.
- * {@see AudioManager#registerRemoteControlDisplay(IRemoteControlDisplay)}.
- */
-oneway interface IRemoteControlDisplay
-{
- /**
- * Sets the generation counter of the current client that is displayed on the remote control.
- * @param clientGeneration the new RemoteControlClient generation
- * @param clientMediaIntent the PendingIntent associated with the client.
- * May be null, which implies there is no registered media button event receiver.
- * @param clearing true if the new client generation value maps to a remote control update
- * where the display should be cleared.
- */
- void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
- boolean clearing);
-
- /**
- * Sets whether the controls of this display are enabled
- * @param if false, the display shouldn't any commands
- */
- void setEnabled(boolean enabled);
-
- /**
- * Sets the playback information (state, position and speed) of a client.
- * @param generationId the current generation ID as known by this client
- * @param state the current playback state, one of the following values:
- * {@link RemoteControlClient#PLAYSTATE_STOPPED},
- * {@link RemoteControlClient#PLAYSTATE_PAUSED},
- * {@link RemoteControlClient#PLAYSTATE_PLAYING},
- * {@link RemoteControlClient#PLAYSTATE_FAST_FORWARDING},
- * {@link RemoteControlClient#PLAYSTATE_REWINDING},
- * {@link RemoteControlClient#PLAYSTATE_SKIPPING_FORWARDS},
- * {@link RemoteControlClient#PLAYSTATE_SKIPPING_BACKWARDS},
- * {@link RemoteControlClient#PLAYSTATE_BUFFERING},
- * {@link RemoteControlClient#PLAYSTATE_ERROR}.
- * @param stateChangeTimeMs the time at which the client reported the playback information
- * @param currentPosMs a 0 or positive value for the current media position expressed in ms
- * Strictly negative values imply that position is not known:
- * a value of {@link RemoteControlClient#PLAYBACK_POSITION_INVALID} is intended to express
- * that an application doesn't know the position (e.g. listening to a live stream of a radio)
- * or that the position information is not applicable (e.g. when state
- * is {@link RemoteControlClient#PLAYSTATE_BUFFERING} and nothing had played yet);
- * a value of {@link RemoteControlClient#PLAYBACK_POSITION_ALWAYS_UNKNOWN} implies that the
- * application uses {@link RemoteControlClient#setPlaybackState(int)} (legacy API) and will
- * never pass a playback position.
- * @param speed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
- * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
- * playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).
- */
- void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs,
- float speed);
-
- /**
- * Sets the transport control flags and playback position capabilities of a client.
- * @param generationId the current generation ID as known by this client
- * @param transportControlFlags bitmask of the transport controls this client supports, see
- * {@link RemoteControlClient#setTransportControlFlags(int)}
- * @param posCapabilities a bit mask for playback position capabilities, see
- * {@link RemoteControlClient#MEDIA_POSITION_READABLE} and
- * {@link RemoteControlClient#MEDIA_POSITION_WRITABLE}
- */
- void setTransportControlInfo(int generationId, int transportControlFlags, int posCapabilities);
-
- void setMetadata(int generationId, in Bundle metadata);
-
- void setArtwork(int generationId, in Bitmap artwork);
-
- /**
- * To combine metadata text and artwork in one binder call
- */
- void setAllMetadata(int generationId, in Bundle metadata, in Bitmap artwork);
-}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index f36d640..9ea6722 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -20,7 +20,6 @@
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -42,7 +41,6 @@
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
@@ -326,6 +324,8 @@
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
private int mOriginalCount;
+ /** Whether the database had any entries in it before the scan started */
+ private boolean mWasEmptyPriorToScan = false;
/** Whether the scanner has set a default sound for the ringer ringtone. */
private boolean mDefaultRingtoneSet;
/** Whether the scanner has set a default sound for the notification ringtone. */
@@ -535,18 +535,6 @@
if (mMtpObjectHandle != 0) {
entry.mRowId = 0;
}
-
- if ((!mDefaultNotificationSet &&
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
- || (!mDefaultRingtoneSet &&
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
- || (!mDefaultAlarmSet &&
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename))) {
- Log.w(TAG, "forcing rescan of " + entry.mPath +
- "since ringtone setting didn't finish");
- scanAlways = true;
- }
-
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
@@ -926,26 +914,6 @@
}
Uri result = null;
boolean needToSetSettings = false;
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
-
if (rowId == 0) {
if (mMtpObjectHandle != 0) {
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -957,6 +925,28 @@
}
values.put(Files.FileColumns.FORMAT, format);
}
+ // Setting a flag in order not to use bulk insert for the file related with
+ // notifications, ringtones, and alarms, because the rowId of the inserted file is
+ // needed.
+ if (mWasEmptyPriorToScan) {
+ if (notifications && !mDefaultNotificationSet) {
+ if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (ringtones && !mDefaultRingtoneSet) {
+ if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (alarms && !mDefaultAlarmSet) {
+ if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ needToSetSettings = true;
+ }
+ }
+ }
+
// New file, insert it.
// Directories need to be inserted before the files they contain, so they
// get priority when bulk inserting.
@@ -1026,20 +1016,13 @@
private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
- if(wasSettingAlreadySet(settingName)) {
- return;
- }
-
String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
settingName);
if (TextUtils.isEmpty(existingSettingValue)) {
// Set the setting to the given URI
-
- ContentResolver cr = mContext.getContentResolver();
- Settings.System.putString(cr, settingName,
+ Settings.System.putString(mContext.getContentResolver(), settingName,
ContentUris.withAppendedId(uri, rowId).toString());
- Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
}
@@ -1067,20 +1050,6 @@
}; // end of anonymous MediaScannerClient instance
- private String settingSetIndicatorName(String base) {
- return base + "_set";
- }
-
- private boolean wasSettingAlreadySet(String name) {
- ContentResolver cr = mContext.getContentResolver();
- String indicatorName = settingSetIndicatorName(name);
- try {
- return Settings.System.getInt(cr, indicatorName) != 0;
- } catch (SettingNotFoundException e) {
- return false;
- }
- }
-
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
Cursor c = null;
String where = null;
@@ -1102,10 +1071,6 @@
selectionArgs = new String[] { "" };
}
- mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
- mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
-
// Tell the provider to not delete the file.
// If the file is truly gone the delete is unnecessary, and we want to avoid
// accidentally deleting files that are really there (this may happen if the
@@ -1124,6 +1089,7 @@
// with CursorWindow positioning.
long lastId = Long.MIN_VALUE;
Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
+ mWasEmptyPriorToScan = true;
while (true) {
selectionArgs[0] = "" + lastId;
@@ -1142,6 +1108,7 @@
if (num == 0) {
break;
}
+ mWasEmptyPriorToScan = false;
while (c.moveToNext()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1293,7 +1260,7 @@
}
}
- private void postscan(final String[] directories) throws RemoteException {
+ private void postscan(String[] directories) throws RemoteException {
// handle playlists last, after we know what media files are on the storage.
if (mProcessPlaylists) {
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c9a86d8..6d32eff 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -349,16 +349,6 @@
*/
public RemoteControlClient(PendingIntent mediaButtonIntent) {
mRcMediaIntent = mediaButtonIntent;
-
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
- }
}
/**
@@ -378,8 +368,6 @@
*/
public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
mRcMediaIntent = mediaButtonIntent;
-
- mEventHandler = new EventHandler(this, looper);
}
/**
@@ -707,39 +695,6 @@
}
}
- // TODO investigate if we still need position drift checking
- private void onPositionDriftCheck() {
- if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
- synchronized(mCacheLock) {
- if ((mEventHandler == null) || (mPositionProvider == null) || !mNeedsPositionSync) {
- return;
- }
- if ((mPlaybackPositionMs < 0) || (mPlaybackSpeed == 0.0f)) {
- if (DEBUG) { Log.d(TAG, " no valid position or 0 speed, no check needed"); }
- return;
- }
- long estPos = mPlaybackPositionMs + (long)
- ((SystemClock.elapsedRealtime() - mPlaybackStateChangeTimeMs) / mPlaybackSpeed);
- long actPos = mPositionProvider.onGetPlaybackPosition();
- if (actPos >= 0) {
- if (Math.abs(estPos - actPos) > POSITION_DRIFT_MAX_MS) {
- // drift happened, report the new position
- if (DEBUG) { Log.w(TAG, " drift detected: actual=" +actPos +" est=" +estPos); }
- setPlaybackState(mPlaybackState, actPos, mPlaybackSpeed);
- } else {
- if (DEBUG) { Log.d(TAG, " no drift: actual=" + actPos +" est=" + estPos); }
- // no drift, schedule the next drift check
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- getCheckPeriodFromSpeed(mPlaybackSpeed));
- }
- } else {
- // invalid position (negative value), can't check for drift
- mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
- }
- }
- }
-
/**
* Sets the flags for the media transport control buttons that this client supports.
* @param transportControlFlags A combination of the following flags:
@@ -856,14 +811,6 @@
public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
mPositionProvider = l;
- if ((mPositionProvider != null) && (mEventHandler != null)
- && playbackPositionShouldMove(mPlaybackState)) {
- // playback position is already moving, but now we have a position provider,
- // so schedule a drift check right now
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- 0 /*check now*/);
- }
}
}
@@ -1001,26 +948,6 @@
}
};
- private EventHandler mEventHandler;
- private final static int MSG_POSITION_DRIFT_CHECK = 11;
-
- private class EventHandler extends Handler {
- public EventHandler(RemoteControlClient rcc, Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_POSITION_DRIFT_CHECK:
- onPositionDriftCheck();
- break;
- default:
- Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
- }
- }
- }
-
//===========================================================
// Message handlers
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index d84cf30..90f2163 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -17,8 +17,6 @@
package android.media;
import android.app.ActivityManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,7 +30,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -61,15 +58,10 @@
@Deprecated public final class RemoteController
{
private final static int MAX_BITMAP_DIMENSION = 512;
- private final static int TRANSPORT_UNKNOWN = 0;
private final static String TAG = "RemoteController";
private final static boolean DEBUG = false;
- private final static boolean USE_SESSIONS = true;
- private final static Object mGenLock = new Object();
private final static Object mInfoLock = new Object();
- private final RcDisplay mRcd;
private final Context mContext;
- private final AudioManager mAudioManager;
private final int mMaxBitmapDimension;
private MetadataEditor mMetadataEditor;
@@ -78,15 +70,9 @@
private MediaController.Callback mSessionCb = new MediaControllerCallback();
/**
- * Synchronized on mGenLock
- */
- private int mClientGenerationIdCurrent = 0;
-
- /**
* Synchronized on mInfoLock
*/
private boolean mIsRegistered = false;
- private PendingIntent mClientPendingIntentCurrent;
private OnClientUpdateListener mOnClientUpdateListener;
private PlaybackInfo mLastPlaybackInfo;
private int mArtworkWidth = -1;
@@ -136,8 +122,6 @@
}
mOnClientUpdateListener = updateListener;
mContext = context;
- mRcd = new RcDisplay(this);
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mSessionManager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
mSessionListener = new TopTransportSessionListener();
@@ -207,22 +191,6 @@
public void onClientMetadataUpdate(MetadataEditor metadataEditor);
};
-
- /**
- * @hide
- */
- public String getRemoteControlClientPackageName() {
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- return mCurrentSession != null ? mCurrentSession.getPackageName()
- : null;
- }
- } else {
- return mClientPendingIntentCurrent != null ?
- mClientPendingIntentCurrent.getCreatorPackage() : null;
- }
- }
-
/**
* Return the estimated playback position of the current media track or a negative value
* if not available.
@@ -240,38 +208,13 @@
* @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float)
*/
public long getEstimatedMediaPosition() {
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- PlaybackState state = mCurrentSession.getPlaybackState();
- if (state != null) {
- return state.getPosition();
- }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ PlaybackState state = mCurrentSession.getPlaybackState();
+ if (state != null) {
+ return state.getPosition();
}
}
- } else {
- final PlaybackInfo lastPlaybackInfo;
- synchronized (mInfoLock) {
- lastPlaybackInfo = mLastPlaybackInfo;
- }
- if (lastPlaybackInfo != null) {
- if (!RemoteControlClient.playbackPositionShouldMove(lastPlaybackInfo.mState)) {
- return lastPlaybackInfo.mCurrentPosMs;
- }
-
- // Take the current position at the time of state change and
- // estimate.
- final long thenPos = lastPlaybackInfo.mCurrentPosMs;
- if (thenPos < 0) {
- return -1;
- }
-
- final long now = SystemClock.elapsedRealtime();
- final long then = lastPlaybackInfo.mStateChangeTimeMs;
- final long sinceThen = now - then;
- final long scaledSinceThen = (long) (sinceThen * lastPlaybackInfo.mSpeed);
- return thenPos + scaledSinceThen;
- }
}
return -1;
}
@@ -308,42 +251,12 @@
if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
throw new IllegalArgumentException("not a media key event");
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
- }
- return false;
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
}
- } else {
- final PendingIntent pi;
- synchronized (mInfoLock) {
- if (!mIsRegistered) {
- Log.e(TAG,
- "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
- return false;
- }
- if (!mEnabled) {
- Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
- return false;
- }
- pi = mClientPendingIntentCurrent;
- }
- if (pi != null) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- pi.send(mContext, 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button down: ", e);
- return false;
- }
- } else {
- Log.i(TAG, "No-op when sending key click, no receiver right now");
- return false;
- }
+ return false;
}
- return true;
}
@@ -453,8 +366,7 @@
Log.e(TAG, "Cannot set synchronization mode on an unregistered RemoteController");
return false;
}
- mAudioManager.remoteControlDisplayWantsPlaybackPositionSync(mRcd,
- POSITION_SYNCHRONIZATION_CHECK == sync);
+ // deprecated, no-op
return true;
}
@@ -541,154 +453,6 @@
}
-
- //==================================================
- // Implementation of IRemoteControlDisplay interface
- private static class RcDisplay extends IRemoteControlDisplay.Stub {
- private final WeakReference<RemoteController> mController;
-
- RcDisplay(RemoteController rc) {
- mController = new WeakReference<RemoteController>(rc);
- }
-
- public void setCurrentClientId(int genId, PendingIntent clientMediaIntent,
- boolean clearing) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- boolean isNew = false;
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- rc.mClientGenerationIdCurrent = genId;
- isNew = true;
- }
- }
- if (clientMediaIntent != null) {
- sendMsg(rc.mEventHandler, MSG_NEW_PENDING_INTENT, SENDMSG_REPLACE,
- genId /*arg1*/, 0, clientMediaIntent /*obj*/, 0 /*delay*/);
- }
- if (isNew || clearing) {
- sendMsg(rc.mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- genId /*arg1*/, clearing ? 1 : 0, null /*obj*/, 0 /*delay*/);
- }
- }
-
- public void setEnabled(boolean enabled) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- sendMsg(rc.mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE,
- enabled ? 1 : 0 /*arg1*/, 0, null /*obj*/, 0 /*delay*/);
- }
-
- public void setPlaybackState(int genId, int state,
- long stateChangeTimeMs, long currentPosMs, float speed) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "> new playback state: genId="+genId
- + " state="+ state
- + " changeTime="+ stateChangeTimeMs
- + " pos=" + currentPosMs
- + "ms speed=" + speed);
- }
-
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final PlaybackInfo playbackInfo =
- new PlaybackInfo(state, stateChangeTimeMs, currentPosMs, speed);
- sendMsg(rc.mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0, playbackInfo /*obj*/, 0 /*delay*/);
-
- }
-
- public void setTransportControlInfo(int genId, int transportControlFlags,
- int posCapabilities) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- sendMsg(rc.mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, transportControlFlags /*arg2*/,
- null /*obj*/, 0 /*delay*/);
- }
-
- public void setMetadata(int genId, Bundle metadata) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.e(TAG, "setMetadata("+genId+")"); }
- if (metadata == null) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
-
- public void setArtwork(int genId, Bitmap artwork) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.v(TAG, "setArtwork("+genId+")"); }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- Bundle metadata = new Bundle(1);
- metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), artwork);
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
-
- public void setAllMetadata(int genId, Bundle metadata, Bitmap artwork) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.e(TAG, "setAllMetadata("+genId+")"); }
- if ((metadata == null) && (artwork == null)) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- if (metadata == null) {
- metadata = new Bundle(1);
- }
- if (artwork != null) {
- metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
- artwork);
- }
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
- }
-
/**
* This receives updates when the current session changes. This is
* registered to receive the updates on the handler thread so it can call
@@ -734,14 +498,9 @@
//==================================================
// Event handling
private final EventHandler mEventHandler;
- private final static int MSG_NEW_PENDING_INTENT = 0;
- private final static int MSG_NEW_PLAYBACK_INFO = 1;
- private final static int MSG_NEW_TRANSPORT_INFO = 2;
- private final static int MSG_NEW_METADATA = 3; // msg always has non-null obj parameter
- private final static int MSG_CLIENT_CHANGE = 4;
- private final static int MSG_DISPLAY_ENABLE = 5;
- private final static int MSG_NEW_PLAYBACK_STATE = 6;
- private final static int MSG_NEW_MEDIA_METADATA = 7;
+ private final static int MSG_CLIENT_CHANGE = 0;
+ private final static int MSG_NEW_PLAYBACK_STATE = 1;
+ private final static int MSG_NEW_MEDIA_METADATA = 2;
private class EventHandler extends Handler {
@@ -752,26 +511,10 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_NEW_PENDING_INTENT:
- onNewPendingIntent(msg.arg1, (PendingIntent) msg.obj);
- break;
- case MSG_NEW_PLAYBACK_INFO:
- onNewPlaybackInfo(msg.arg1, (PlaybackInfo) msg.obj);
- break;
- case MSG_NEW_TRANSPORT_INFO:
- onNewTransportInfo(msg.arg1, msg.arg2);
- break;
- case MSG_NEW_METADATA:
- onNewMetadata(msg.arg1, (Bundle)msg.obj);
- break;
case MSG_CLIENT_CHANGE:
- onClientChange(msg.arg1, msg.arg2 == 1);
- break;
- case MSG_DISPLAY_ENABLE:
- onDisplayEnable(msg.arg1 == 1);
+ onClientChange(msg.arg2 == 1);
break;
case MSG_NEW_PLAYBACK_STATE:
- // same as new playback info but using new apis
onNewPlaybackState((PlaybackState) msg.obj);
break;
case MSG_NEW_MEDIA_METADATA:
@@ -835,100 +578,7 @@
handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs);
}
- ///////////// These calls are used by the old APIs with RCC and RCD //////////////////////
- private void onNewPendingIntent(int genId, PendingIntent pi) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- synchronized(mInfoLock) {
- mClientPendingIntentCurrent = pi;
- }
- }
-
- private void onNewPlaybackInfo(int genId, PlaybackInfo pi) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- l = this.mOnClientUpdateListener;
- mLastPlaybackInfo = pi;
- }
- if (l != null) {
- if (pi.mCurrentPosMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
- l.onClientPlaybackStateUpdate(pi.mState);
- } else {
- l.onClientPlaybackStateUpdate(pi.mState, pi.mStateChangeTimeMs, pi.mCurrentPosMs,
- pi.mSpeed);
- }
- }
- }
-
- private void onNewTransportInfo(int genId, int transportControlFlags) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- l = mOnClientUpdateListener;
- }
- if (l != null) {
- l.onClientTransportControlUpdate(transportControlFlags);
- }
- }
-
- /**
- * @param genId
- * @param metadata guaranteed to be always non-null
- */
- private void onNewMetadata(int genId, Bundle metadata) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- final MetadataEditor metadataEditor;
- // prepare the received Bundle to be used inside a MetadataEditor
- final long editableKeys = metadata.getLong(
- String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK), 0);
- if (editableKeys != 0) {
- metadata.remove(String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK));
- }
- synchronized(mInfoLock) {
- l = mOnClientUpdateListener;
- if ((mMetadataEditor != null) && (mMetadataEditor.mEditorMetadata != null)) {
- if (mMetadataEditor.mEditorMetadata != metadata) {
- // existing metadata, merge existing and new
- mMetadataEditor.mEditorMetadata.putAll(metadata);
- }
-
- mMetadataEditor.putBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK,
- (Bitmap)metadata.getParcelable(
- String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK)));
- mMetadataEditor.cleanupBitmapFromBundle(MediaMetadataEditor.BITMAP_KEY_ARTWORK);
- } else {
- mMetadataEditor = new MetadataEditor(metadata, editableKeys);
- }
- metadataEditor = mMetadataEditor;
- }
- if (l != null) {
- l.onClientMetadataUpdate(metadataEditor);
- }
- }
-
- private void onClientChange(int genId, boolean clearing) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
+ private void onClientChange(boolean clearing) {
final OnClientUpdateListener l;
synchronized(mInfoLock) {
l = mOnClientUpdateListener;
@@ -939,39 +589,6 @@
}
}
- private void onDisplayEnable(boolean enabled) {
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- mEnabled = enabled;
- l = this.mOnClientUpdateListener;
- }
- if (!enabled) {
- // when disabling, reset all info sent to the user
- final int genId;
- synchronized (mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- // send "stopped" state, happened "now", playback position is 0, speed 0.0f
- final PlaybackInfo pi = new PlaybackInfo(RemoteControlClient.PLAYSTATE_STOPPED,
- SystemClock.elapsedRealtime() /*stateChangeTimeMs*/,
- 0 /*currentPosMs*/, 0.0f /*speed*/);
- sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0 /*arg2, ignored*/, pi /*obj*/, 0 /*delay*/);
- // send "blank" transport control info: no controls are supported
- sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0 /*arg2, no flags*/,
- null /*obj, ignored*/, 0 /*delay*/);
- // send dummy metadata with empty string for title and artist, duration of 0
- Bundle metadata = new Bundle(3);
- metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE), "");
- metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST), "");
- metadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION), 0);
- sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2, ignored*/, metadata /*obj*/, 0 /*delay*/);
- }
- }
-
- ///////////// These calls are used by the new APIs with Sessions //////////////////////
private void updateController(MediaController controller) {
if (DEBUG) {
Log.d(TAG, "Updating controller to " + controller + " previous controller is "
@@ -983,7 +600,7 @@
mCurrentSession.unregisterCallback(mSessionCb);
mCurrentSession = null;
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- 0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 1 /* clearing */, null /* obj */, 0 /* delay */);
}
} else if (mCurrentSession == null
|| !controller.getSessionToken()
@@ -992,17 +609,17 @@
mCurrentSession.unregisterCallback(mSessionCb);
}
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- 0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* clearing */, null /* obj */, 0 /* delay */);
mCurrentSession = controller;
mCurrentSession.registerCallback(mSessionCb, mEventHandler);
PlaybackState state = controller.getPlaybackState();
sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE,
- 0 /* genId */, 0, state /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* arg2 ignored */, state /* obj */, 0 /* delay */);
MediaMetadata metadata = controller.getMetadata();
sendMsg(mEventHandler, MSG_NEW_MEDIA_METADATA, SENDMSG_REPLACE,
- 0 /* arg1 */, 0 /* arg2 */, metadata /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* arg2 ignored*/, metadata /* obj */, 0 /*delay*/);
}
// else same controller, no need to update
}
@@ -1069,38 +686,6 @@
/**
* @hide
- * Used by AudioManager to mark this instance as registered.
- * @param registered
- */
- void setIsRegistered(boolean registered) {
- synchronized (mInfoLock) {
- mIsRegistered = registered;
- }
- }
-
- /**
- * @hide
- * Used by AudioManager to access binder to be registered/unregistered inside MediaFocusControl
- * @return
- */
- RcDisplay getRcDisplay() {
- return mRcd;
- }
-
- /**
- * @hide
- * Used by AudioManager to read the current artwork dimension
- * @return array containing width (index 0) and height (index 1) of currently set artwork size
- */
- int[] getArtworkSize() {
- synchronized (mInfoLock) {
- int[] size = { mArtworkWidth, mArtworkHeight };
- return size;
- }
- }
-
- /**
- * @hide
* Used by AudioManager to access user listener receiving the client update notifications
* @return
*/
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 1355635..3164930 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -166,7 +166,7 @@
updateAppOpsPlayAudio();
// register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
mAppOpsCallback = new IAppOpsCallback.Stub() {
- public void opChanged(int op, String packageName) {
+ public void opChanged(int op, int uid, String packageName) {
synchronized (mLock) {
if (op == AppOpsManager.OP_PLAY_AUDIO) {
updateAppOpsPlayAudio();
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index e0a8026..41b8ab2 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -44,7 +44,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
-import java.util.Collections;
import java.util.List;
/**
@@ -519,13 +518,10 @@
return;
}
- List<MediaItem> data = list.getList();
+ List<MediaItem> data = list == null ? null : list.getList();
if (DBG) {
Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
}
- if (data == null) {
- data = Collections.emptyList();
- }
// Check that the subscription is still subscribed.
final Subscription subscription = mSubscriptions.get(parentId);
@@ -730,10 +726,9 @@
* 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.
+ * @param children The children which were loaded, or null if the id is invalid.
*/
- public void onChildrenLoaded(@NonNull String parentId,
- @NonNull List<MediaItem> children) {
+ public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children) {
}
/**
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 5ad1bce..f8c6f3f 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -87,7 +87,7 @@
return;
}
- long startTime = System.currentTimeMillis();
+ long startTime = System.nanoTime();
switch (msg.what) {
case DO_RELEASE: {
mTvInputSessionImpl.release();
@@ -185,18 +185,18 @@
break;
}
}
- long duration = System.currentTimeMillis() - startTime;
- if (duration > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
+ long durationMs = (System.nanoTime() - startTime) / (1000 * 1000);
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration="
- + duration + "ms)");
- if (msg.what == DO_TUNE && duration > EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS) {
- throw new RuntimeException("Too much time to handle tune request. (" + duration
+ + durationMs + "ms)");
+ if (msg.what == DO_TUNE && durationMs > EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS) {
+ throw new RuntimeException("Too much time to handle tune request. (" + durationMs
+ "ms > " + EXECUTE_MESSAGE_TUNE_TIMEOUT_MILLIS + "ms) "
+ "Consider handling the tune request in a separate thread.");
}
- if (duration > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
throw new RuntimeException("Too much time to handle a request. (type=" + msg.what +
- ", " + duration + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
+ ", " + durationMs + "ms > " + EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS + "ms).");
}
}
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 043b80e..6197c70 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -931,9 +931,7 @@
*
* @param rating The {@link TvContentRating} to check.
* @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
- * @hide
*/
- @SystemApi
public final boolean contains(@NonNull TvContentRating rating) {
Preconditions.checkNotNull(rating);
if (!rating.getMainRating().equals(mRating)) {
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 1bb99ff..f2c6a6c 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -469,10 +469,6 @@
= new Result<List<MediaBrowser.MediaItem>>(parentId) {
@Override
void onResultSent(List<MediaBrowser.MediaItem> list) {
- if (list == null) {
- throw new IllegalStateException("onLoadChildren sent null list for id "
- + parentId);
- }
if (mConnections.get(connection.callbacks.asBinder()) != connection) {
if (DBG) {
Log.d(TAG, "Not sending onLoadChildren result for connection that has"
@@ -481,7 +477,8 @@
return;
}
- final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(list);
+ final ParceledListSlice<MediaBrowser.MediaItem> pls =
+ list == null ? null : new ParceledListSlice(list);
try {
connection.callbacks.onLoadChildren(parentId, pls);
} catch (RemoteException ex) {
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index e67c554..ccf1501 100644
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -12,7 +12,9 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application android:label="@string/service_name"
- android:allowBackup="false">
+ android:allowBackup="false"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".DefaultContainerService"
android:enabled="true"
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index d45345e..595928a 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -71,7 +71,7 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
- <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
+ <action android:name="android.provider.action.BROWSE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.document/root" />
</intent-filter>
diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml
new file mode 100644
index 0000000..0a258ac
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_root_home.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M20 6h-8l-2-2H4c-1.1 0-1.99 .9 -1.99 2L2 18c0 1.1 .9 2 2 2h16c1.1 0 2-.9
+2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4
+8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/item_root_background.xml b/packages/DocumentsUI/res/drawable/item_root_background.xml
deleted file mode 100644
index c403159..0000000
--- a/packages/DocumentsUI/res/drawable/item_root_background.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:state_activated="true">
- <color android:color="@color/material_grey_300" />
- </item>
- <item android:state_focused="false" android:state_activated="true">
- <color android:color="@color/material_grey_300" />
- </item>
-</selector>
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 381e1c89..fe06eaf 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -17,7 +17,7 @@
<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background"
+ android:background="@color/item_doc_background"
android:orientation="horizontal"
android:focusable="true">
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index 0dac0d5..0146f14 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -61,7 +61,7 @@
android:layout_gravity="start"
android:orientation="vertical"
android:elevation="16dp"
- android:background="@*android:color/white">
+ android:background="@color/window_background">
<Toolbar
android:id="@+id/roots_toolbar"
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 403c667..3135977 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -50,9 +50,7 @@
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
- android:baselineAligned="false"
- android:divider="?android:attr/dividerVertical"
- android:showDividers="middle">
+ android:baselineAligned="false">
<FrameLayout
android:id="@+id/container_roots"
@@ -62,8 +60,7 @@
<include layout="@layout/directory_cluster"
android:layout_width="0dp"
android:layout_weight="1"
- android:elevation="8dp"
- android:background="@color/material_grey_50" />
+ android:elevation="8dp" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index ada7f49..f9bbccb 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -17,7 +17,6 @@
<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/material_grey_50"
android:orientation="vertical"
android:animateLayoutChanges="true">
@@ -78,8 +77,7 @@
android:paddingBottom="0dp"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
- android:drawSelectorOnTop="true"
- android:background="@color/directory_background" />
+ android:drawSelectorOnTop="true" />
</FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index 1dfb34a..dcd5cfd 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -18,101 +18,101 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_item_margin"
- android:background="@color/item_doc_grid_background"
+ android:background="@color/item_doc_background"
android:focusable="true">
<!-- Main item thumbnail. Comprised of two overlapping images, the
visibility of which is controlled by code in
DirectoryFragment.java. -->
+
<FrameLayout
android:id="@+id/thumbnail"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="8dp">
-
+ android:layout_height="wrap_content">
+
<com.android.documentsui.GridItemThumbnail
android:id="@+id/icon_thumb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:contentDescription="@null" />
-
- <ImageView
+
+ <com.android.documentsui.GridItemThumbnail
android:id="@+id/icon_mime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerInside"
android:contentDescription="@null" />
-
+
</FrameLayout>
-
+
<!-- Item nameplate. Has a mime-type icon and some text fields (title,
size, mod-time, etc). -->
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/thumbnail"
- android:layout_toEndOf="@android:id/icon1"
- android:singleLine="true"
- android:ellipsize="middle"
- android:textAlignment="viewStart"
- android:paddingEnd="12dp"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="@*android:color/primary_text_default_material_light" />
- <TextView
- android:id="@+id/size"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_toEndOf="@android:id/icon1"
- android:paddingEnd="4dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_light" />
-
- <TextView
- android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_toEndOf="@id/size"
- android:paddingEnd="12dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_light" />
-
- <ImageView
- android:id="@android:id/icon1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:layout_below="@id/thumbnail"
- android:layout_alignParentLeft="true"
- android:layout_alignBottom="@id/size"
- android:layout_alignTop="@android:id/title"
- android:scaleType="centerInside"
- android:contentDescription="@null"
- android:paddingStart="12dp"
- android:paddingEnd="8dp"/>
-
- <!-- Use an explicit spacer so we can align things to it. -->
- <Space
- android:id="@+id/bottomPadding"
+ <RelativeLayout
+ android:id="@+id/nameplate"
android:layout_width="match_parent"
- android:layout_height="8dp"
- android:layout_below="@id/size" />
+ android:layout_height="wrap_content"
+ android:layout_below="@id/thumbnail"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp">
+
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="8dp"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerInside"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@android:id/icon1"
+ android:singleLine="true"
+ android:ellipsize="middle"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textColor="@*android:color/primary_text_default_material_light" />
+
+ <TextView
+ android:id="@+id/size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@android:id/icon1"
+ android:layout_below="@android:id/title"
+ android:layout_marginEnd="4dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@*android:color/primary_text_default_material_light" />
+
+ <TextView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_toEndOf="@id/size"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@*android:color/primary_text_default_material_light" />
+
+ </RelativeLayout>
<!-- An overlay that draws the item border when it is focused. -->
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignBottom="@id/bottomPadding"
+ android:layout_alignBottom="@id/nameplate"
android:layout_alignTop="@id/thumbnail"
android:layout_alignLeft="@id/thumbnail"
android:layout_alignRight="@id/thumbnail"
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c409166..e068423 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -17,10 +17,10 @@
<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background"
+ android:background="@color/item_doc_background"
android:orientation="horizontal"
android:focusable="true">
-
+
<View
android:id="@+id/focus_indicator"
android:layout_width="4dp"
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index 90b1ff6..ff80d07 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -22,8 +22,7 @@
android:paddingEnd="@dimen/list_item_padding"
android:gravity="center_vertical"
android:orientation="horizontal"
- android:baselineAligned="false"
- android:background="@drawable/item_root_background">
+ android:baselineAligned="false">
<FrameLayout
android:layout_width="@dimen/icon_size"
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 68c8b65..153c673 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -17,14 +17,20 @@
<resources>
<color name="material_grey_400">#ffbdbdbd</color>
+ <!-- This is the window background, but also the background for anything
+ else that needs to manually declare a background matching the "default"
+ app background (e.g. the drawer overlay). -->
+ <color name="window_background">#fff1f1f1</color>
+
<color name="primary_dark">@*android:color/primary_dark_material_dark</color>
<color name="primary">@*android:color/material_blue_grey_900</color>
<color name="accent">@*android:color/accent_material_light</color>
<color name="action_mode">@color/material_grey_400</color>
-
- <color name="directory_background">@*android:color/material_grey_300</color>
- <color name="item_doc_grid_background">@android:color/white</color>
- <color name="item_doc_grid_protect_background">@android:color/white</color>
+
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
+
+ <color name="item_doc_background">#fffafafa</color>
+ <color name="item_doc_background_selected">#ffe0f2f1</color>
+
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 15d17cc..6712e2d 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -25,6 +25,7 @@
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
+ <item name="android:windowBackground">@color/window_background</item>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
@@ -44,6 +45,7 @@
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
+ <item name="android:windowBackground">@color/window_background</item>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 0ee970d..91ac033 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -358,18 +358,6 @@
return mState;
}
- public static abstract class DocumentsIntent {
- /** Intent action name to open copy destination. */
- public static String ACTION_OPEN_COPY_DESTINATION =
- "com.android.documentsui.OPEN_COPY_DESTINATION";
-
- /**
- * Extra boolean flag for ACTION_OPEN_COPY_DESTINATION_STRING, which
- * specifies if the destination directory needs to create new directory or not.
- */
- public static String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
- }
-
void setDisplayAdvancedDevices(boolean display) {
LocalPreferences.setDisplayAdvancedDevices(this, display);
mState.showAdvanced = mState.forceAdvanced | display;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 55a123f..55e2f44 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -179,8 +179,7 @@
if (mFailedFiles.size() > 0) {
Log.e(TAG, mFailedFiles.size() + " files failed to copy");
final Context context = getApplicationContext();
- final Intent navigateIntent = new Intent(context, FilesActivity.class);
- navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
+ final Intent navigateIntent = buildNavigateIntent(context, stack);
navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
@@ -228,8 +227,7 @@
mIsCancelled = false;
final Context context = getApplicationContext();
- final Intent navigateIntent = new Intent(context, FilesActivity.class);
- navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
+ final Intent navigateIntent = buildNavigateIntent(context, stack);
final String contentTitle = getString(copying ? R.string.copy_notification_title
: R.string.move_notification_title);
@@ -592,4 +590,14 @@
}
}
}
+
+ /**
+ * Creates an intent for navigating back to the destination directory.
+ */
+ private Intent buildNavigateIntent(Context context, DocumentStack stack) {
+ Intent intent = new Intent(context, FilesActivity.class);
+ intent.setAction(DocumentsContract.ACTION_BROWSE);
+ intent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
+ return intent;
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 13c481c..4f4649c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -19,7 +19,7 @@
import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_GET_CONTENT;
import static com.android.documentsui.State.ACTION_OPEN;
-import static com.android.documentsui.State.ACTION_OPEN_COPY_DESTINATION;
+import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
@@ -123,7 +123,7 @@
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
} else if (mState.action == ACTION_OPEN_TREE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
PickFragment.show(getFragmentManager());
}
@@ -135,7 +135,7 @@
} else if (mState.action == ACTION_OPEN ||
mState.action == ACTION_CREATE ||
mState.action == ACTION_OPEN_TREE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
RootsFragment.show(getFragmentManager(), null);
}
@@ -163,8 +163,8 @@
state.action = ACTION_GET_CONTENT;
} else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
state.action = ACTION_OPEN_TREE;
- } else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
- state.action = ACTION_OPEN_COPY_DESTINATION;
+ } else if (Shared.ACTION_PICK_COPY_DESTINATION.equals(action)) {
+ state.action = ACTION_PICK_COPY_DESTINATION;
}
if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
@@ -172,9 +172,14 @@
Intent.EXTRA_ALLOW_MULTIPLE, false);
}
- if (state.action == ACTION_OPEN_COPY_DESTINATION) {
+ if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT
+ || state.action == ACTION_CREATE) {
+ state.openableOnly = intent.hasCategory(Intent.CATEGORY_OPENABLE);
+ }
+
+ if (state.action == ACTION_PICK_COPY_DESTINATION) {
state.directoryCopy = intent.getBooleanExtra(
- BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
+ Shared.EXTRA_DIRECTORY_COPY, false);
state.transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
CopyService.TRANSFER_MODE_COPY);
}
@@ -257,7 +262,7 @@
mState.action == ACTION_OPEN_TREE) {
mRootsToolbar.setTitle(R.string.title_open);
} else if (mState.action == ACTION_CREATE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
mRootsToolbar.setTitle(R.string.title_save);
}
}
@@ -324,7 +329,7 @@
boolean recents = cwd == null;
boolean picking = mState.action == ACTION_CREATE
|| mState.action == ACTION_OPEN_TREE
- || mState.action == ACTION_OPEN_COPY_DESTINATION;
+ || mState.action == ACTION_PICK_COPY_DESTINATION;
createDir.setVisible(picking && !recents && cwd.isCreateSupported());
mSearchManager.showMenu(!picking);
@@ -361,7 +366,7 @@
// No directory means recents
if (mState.action == ACTION_CREATE ||
mState.action == ACTION_OPEN_TREE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
RecentsCreateFragment.show(fm);
} else {
DirectoryFragment.showRecentsOpen(fm, anim);
@@ -391,7 +396,7 @@
}
if (mState.action == ACTION_OPEN_TREE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
final PickFragment pick = PickFragment.get(fm);
if (pick != null) {
pick.setPickTarget(mState.action, mState.transferMode, cwd);
@@ -444,7 +449,7 @@
if (mState.action == ACTION_OPEN_TREE) {
result = DocumentsContract.buildTreeDocumentUri(
pickTarget.authority, pickTarget.documentId);
- } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
result = pickTarget.derivedUri;
} else {
// Should not be reached.
@@ -461,7 +466,7 @@
final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
if (mState.action == ACTION_CREATE ||
mState.action == ACTION_OPEN_TREE ||
- mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ mState.action == ACTION_PICK_COPY_DESTINATION) {
// Remember stack for last create
values.clear();
values.put(RecentColumns.KEY, mState.stack.buildKey());
@@ -500,7 +505,7 @@
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
+ } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
// Picking a copy destination is only used internally by us, so we
// don't need to extend permissions to the caller.
intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index 49dae3d..1b5b60de 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -117,6 +117,8 @@
public MotionInputEvent(MotionEvent event, RecyclerView view) {
mEvent = event;
mView = view;
+
+ // Consider determining position lazily as an optimization.
View child = mView.findChildViewUnder(mEvent.getX(), mEvent.getY());
mPosition = (child != null)
? mView.getChildAdapterPosition(child)
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 48e28dc..bbf4682 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -110,7 +110,7 @@
mPick.setText(R.string.button_select);
mCancel.setVisibility(View.GONE);
break;
- case State.ACTION_OPEN_COPY_DESTINATION:
+ case State.ACTION_PICK_COPY_DESTINATION:
mPick.setText(R.string.button_copy);
mCancel.setVisibility(View.VISIBLE);
break;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 4fc3788..72ee6cbab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -360,7 +360,7 @@
// Exclude read-only devices when creating
if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
- if (state.action == State.ACTION_OPEN_COPY_DESTINATION && !supportsCreate) continue;
+ if (state.action == State.ACTION_PICK_COPY_DESTINATION && !supportsCreate) continue;
// Exclude roots that don't support directory picking
if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue;
// Exclude advanced devices when not requested
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index beff196..4c844c4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -297,7 +297,7 @@
for (final RootInfo root : roots) {
final RootItem item = new RootItem(root);
- if (root.isLibrary()) {
+ if (root.isLibrary() || root.isHome()) {
libraries.add(item);
} else {
others.add(item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index a4d6dc5..570c9bf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -20,6 +20,16 @@
/** @hide */
public final class Shared {
+ /** Intent action name to pick a copy destination. */
+ public static final String ACTION_PICK_COPY_DESTINATION =
+ "com.android.documentsui.PICK_COPY_DESTINATION";
+
+ /**
+ * Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
+ * specifies if the destination directory needs to create new directory or not.
+ */
+ public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
+
public static final boolean DEBUG = true;
public static final String TAG = "Documents";
public static final String EXTRA_STACK = "com.android.documentsui.STACK";
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 4306a0e..c81d4fb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -52,6 +52,7 @@
public boolean stackTouched;
public boolean restored;
public boolean directoryCopy;
+ public boolean openableOnly;
/** Transfer mode for file copy/move operations. */
public int transferMode;
@@ -75,7 +76,7 @@
public static final int ACTION_OPEN_TREE = 4;
public static final int ACTION_MANAGE = 5;
public static final int ACTION_BROWSE = 6;
- public static final int ACTION_OPEN_COPY_DESTINATION = 8;
+ public static final int ACTION_PICK_COPY_DESTINATION = 8;
public static final int MODE_UNKNOWN = 0;
public static final int MODE_LIST = 1;
@@ -119,6 +120,7 @@
out.writeMap(dirState);
out.writeList(selectedDocumentsForCopy);
out.writeList(excludedAuthorities);
+ out.writeInt(openableOnly ? 1 : 0);
}
public static final Creator<State> CREATOR = new Creator<State>() {
@@ -142,6 +144,7 @@
in.readMap(state.dirState, null);
in.readList(state.selectedDocumentsForCopy, null);
in.readList(state.excludedAuthorities, null);
+ state.openableOnly = in.readInt() != 0;
return state;
}
@@ -150,4 +153,4 @@
return new State[size];
}
};
-}
\ No newline at end of file
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 21420c8..b0421b0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -111,8 +111,8 @@
import com.android.documentsui.State;
import com.android.documentsui.ThumbnailCache;
import com.android.documentsui.BaseActivity.DocumentContext;
-import com.android.documentsui.BaseActivity.DocumentsIntent;
import com.android.documentsui.ProviderExecutor.Preemptable;
+import com.android.documentsui.Shared;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.dirlist.MultiSelectManager.Callback;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
@@ -660,8 +660,7 @@
checkNotNull(cursor, "Cursor cannot be null.");
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- return mTuner.canSelectType(docMimeType)
- && mTuner.isDocumentEnabled(docMimeType, docFlags);
+ return mTuner.canSelectType(docMimeType, docFlags);
}
return true;
}
@@ -897,7 +896,7 @@
// Pop up a dialog to pick a destination. This is inadequate but works for now.
// TODO: Implement a picker that is to spec.
final Intent intent = new Intent(
- BaseActivity.DocumentsIntent.ACTION_OPEN_COPY_DESTINATION,
+ Shared.ACTION_PICK_COPY_DESTINATION,
Uri.EMPTY,
getActivity(),
DocumentsActivity.class);
@@ -914,7 +913,7 @@
break;
}
}
- intent.putExtra(BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, directoryCopy);
+ intent.putExtra(Shared.EXTRA_DIRECTORY_COPY, directoryCopy);
intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mode);
startActivityForResult(intent, REQUEST_COPY_DESTINATION);
}
@@ -943,7 +942,6 @@
public void setSelected(boolean selected) {
itemView.setActivated(selected);
- itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
}
@Override
@@ -1080,8 +1078,6 @@
holder.setSelected(isSelected(position));
- final View line2 = itemView.findViewById(R.id.line2);
-
final ImageView iconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
final ImageView iconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
final TextView title = (TextView) itemView.findViewById(android.R.id.title);
@@ -1138,14 +1134,11 @@
getDocumentIcon(mContext, docAuthority, docId, docMimeType, docIcon, state));
}
- boolean hasLine2 = false;
-
- final boolean hideTitle = (state.derivedMode == MODE_GRID) && mHideGridTitles;
- if (!hideTitle) {
+ if ((state.derivedMode == MODE_GRID) && mHideGridTitles) {
+ title.setVisibility(View.GONE);
+ } else {
title.setText(docDisplayName);
title.setVisibility(View.VISIBLE);
- } else {
- title.setVisibility(View.GONE);
}
Drawable iconDrawable = null;
@@ -1161,7 +1154,6 @@
if (alwaysShowSummary) {
summary.setText(root.getDirectoryString());
summary.setVisibility(View.VISIBLE);
- hasLine2 = true;
} else {
if (iconDrawable != null && roots.isIconUniqueBlocking(root)) {
// No summary needed if icon speaks for itself
@@ -1170,7 +1162,6 @@
summary.setText(root.getDirectoryString());
summary.setVisibility(View.VISIBLE);
summary.setTextAlignment(TextView.TEXT_ALIGNMENT_TEXT_END);
- hasLine2 = true;
}
}
}
@@ -1187,48 +1178,37 @@
if (docSummary != null) {
summary.setText(docSummary);
summary.setVisibility(View.VISIBLE);
- hasLine2 = true;
} else {
summary.setVisibility(View.INVISIBLE);
}
}
}
- if (icon1 != null) icon1.setVisibility(View.GONE);
-
if (iconDrawable != null) {
icon1.setVisibility(View.VISIBLE);
icon1.setImageDrawable(iconDrawable);
+ } else {
+ icon1.setVisibility(View.GONE);
}
if (docLastModified == -1) {
date.setText(null);
} else {
date.setText(formatTime(mContext, docLastModified));
- hasLine2 = true;
}
- if (state.showSize) {
- size.setVisibility(View.VISIBLE);
- if (Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
- size.setText(null);
- } else {
- size.setText(Formatter.formatFileSize(mContext, docSize));
- hasLine2 = true;
- }
- } else {
+ if (!state.showSize || Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
size.setVisibility(View.GONE);
- }
-
- if (line2 != null) {
- line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
+ } else {
+ size.setVisibility(View.VISIBLE);
+ size.setText(Formatter.formatFileSize(mContext, docSize));
}
setEnabledRecursive(itemView, enabled);
iconMime.setAlpha(iconAlpha);
iconThumb.setAlpha(iconAlpha);
- if (icon1 != null) icon1.setAlpha(iconAlpha);
+ icon1.setAlpha(iconAlpha);
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDocumentView(itemView, cursor);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java
index 0963845..1135c21 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryItemAnimator.java
@@ -25,6 +25,8 @@
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
+import com.android.documentsui.R;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -43,12 +45,8 @@
private final Integer mSelectedColor;
public DirectoryItemAnimator(Context context) {
- mDefaultColor = context.getResources().getColor(android.R.color.transparent);
- // Get the accent color.
- TypedValue selColor = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.colorAccent, selColor, true);
- // Set the opacity to 10%.
- mSelectedColor = (selColor.data & 0x00ffffff) | 0x16000000;
+ mDefaultColor = context.getResources().getColor(R.color.item_doc_background);
+ mSelectedColor = context.getResources().getColor(R.color.item_doc_background_selected);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index a0ff165..38d3805 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -63,7 +63,7 @@
// Subtly different from isDocumentEnabled. The reason may be illuminated as follows.
// A folder is enabled such that it may be double clicked, even in settings
// when the folder itself cannot be selected. This may also be true of container types.
- public boolean canSelectType(String docMimeType) {
+ public boolean canSelectType(String docMimeType, int docFlags) {
return true;
}
@@ -85,31 +85,44 @@
}
@Override
- public boolean canSelectType(String docMimeType) {
- switch (mState.action) {
- case ACTION_OPEN:
- case ACTION_CREATE:
- case ACTION_GET_CONTENT:
- return !isDirectory(docMimeType);
- case ACTION_OPEN_TREE:
- // In this case nothing *ever* is selectable...the expected user behavior is
- // they navigate *into* a folder, then click a confirmation button indicating
- // that the current directory is the directory they are picking.
- return false;
+ public boolean canSelectType(String docMimeType, int docFlags) {
+ if (!isDocumentEnabled(docMimeType, docFlags)) {
+ return false;
}
+
+ if (isDirectory(docMimeType)) {
+ return false;
+ }
+
+ if (mState.action == ACTION_OPEN_TREE) {
+ // In this case nothing *ever* is selectable...the expected user behavior is
+ // they navigate *into* a folder, then click a confirmation button indicating
+ // that the current directory is the directory they are picking.
+ return false;
+ }
+
return true;
}
@Override
public boolean isDocumentEnabled(String docMimeType, int docFlags) {
- // Directories are always enabled
+ // Directories are always enabled.
if (isDirectory(docMimeType)) {
return true;
}
- // Read-only files are disabled when creating
- if (mState.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
- return false;
+ switch (mState.action) {
+ case ACTION_CREATE:
+ // Read-only files are disabled when creating.
+ if ((docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) {
+ return false;
+ }
+ case ACTION_OPEN:
+ case ACTION_GET_CONTENT:
+ final boolean isVirtual = (docFlags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
+ if (isVirtual && mState.openableOnly) {
+ return false;
+ }
}
return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index 9eafcc3..65e1a28 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -41,10 +41,9 @@
import android.view.MotionEvent;
import android.view.View;
-import com.android.documentsui.Events;
-import com.android.documentsui.R;
import com.android.documentsui.Events.InputEvent;
import com.android.documentsui.Events.MotionInputEvent;
+import com.android.documentsui.R;
import java.util.ArrayList;
import java.util.Collections;
@@ -88,9 +87,7 @@
* @param mode Selection mode
*/
public MultiSelectManager(final RecyclerView recyclerView, int mode) {
- this(recyclerView.getAdapter(), mode);
-
- mEnvironment = new RuntimeSelectionEnvironment(recyclerView);
+ this(recyclerView.getAdapter(), new RuntimeSelectionEnvironment(recyclerView), mode);
if (mode == MODE_MULTIPLE) {
mBandManager = new BandController();
@@ -137,16 +134,15 @@
/**
* Constructs a new instance with {@code adapter} and {@code helper}.
+ * @param runtimeSelectionEnvironment
* @hide
*/
@VisibleForTesting
- MultiSelectManager(Adapter<?> adapter, int mode) {
- checkNotNull(adapter, "'adapter' cannot be null.");
-
+ MultiSelectManager(Adapter<?> adapter, SelectionEnvironment environment, int mode) {
+ mAdapter = checkNotNull(adapter, "'adapter' cannot be null.");
+ mEnvironment = checkNotNull(environment, "'environment' cannot be null.");
mSingleSelect = mode == MODE_SINGLE;
- mAdapter = adapter;
-
mAdapter.registerAdapterDataObserver(
new AdapterDataObserver() {
@@ -880,7 +876,7 @@
void focusItem(int position);
}
- /** RvFacade implementation backed by good ol' RecyclerView. */
+ /** Recycler view facade implementation backed by good ol' RecyclerView. */
private static final class RuntimeSelectionEnvironment implements SelectionEnvironment {
private final RecyclerView mView;
@@ -1960,11 +1956,50 @@
return false;
}
- int target = RecyclerView.NO_POSITION;
+ // Here we unpack information from the event and pass it to an more
+ // easily tested method....basically eliminating the need to synthesize
+ // events and views and so on in our tests.
+ int position = findTargetPosition(view, keyCode);
+ if (position == RecyclerView.NO_POSITION) {
+ // If there is no valid navigation target, don't handle the keypress.
+ return false;
+ }
+
+ return attemptChangePosition(position, event.isShiftPressed());
+ }
+
+ @VisibleForTesting
+ boolean attemptChangePosition(int targetPosition, boolean isShiftPressed) {
+ // Focus the new file.
+ mEnvironment.focusItem(targetPosition);
+
+ if (isShiftPressed) {
+ if (!hasSelection()) {
+ // If there is no selection, start a selection when the user presses shift-arrow.
+ toggleSelection(targetPosition);
+ } else if (!mSingleSelect) {
+ mRanger.snapSelection(targetPosition);
+ notifySelectionChanged();
+ } else {
+ // We're in single select and have an existing selection.
+ // Our best guess as to what the user would expect is to advance the selection.
+ clearSelection();
+ toggleSelection(targetPosition);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the adapter position that the key combo is targeted at.
+ */
+ private int findTargetPosition(View view, int keyCode) {
+ int position = RecyclerView.NO_POSITION;
if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
- target = 0;
+ position = 0;
} else if (keyCode == KeyEvent.KEYCODE_MOVE_END) {
- target = mAdapter.getItemCount() - 1;
+ position = mAdapter.getItemCount() - 1;
} else {
// Find a navigation target based on the arrow key that the user pressed. Ignore
// navigation targets that aren't items in the recycler view.
@@ -1988,30 +2023,10 @@
// TargetView can be null, for example, if the user pressed <down> at the bottom of
// the list.
if (targetView != null) {
- target = mEnvironment.getAdapterPositionForChildView(targetView);
+ position = mEnvironment.getAdapterPositionForChildView(targetView);
}
}
}
-
- if (target == RecyclerView.NO_POSITION) {
- // If there is no valid navigation target, don't handle the keypress.
- return false;
- }
-
- // Focus the new file.
- mEnvironment.focusItem(target);
-
- if (event.isShiftPressed()) {
- if (!hasSelection()) {
- // If there is no selection, start a selection when the user presses shift-arrow.
- toggleSelection(mEnvironment.getAdapterPositionForChildView(view));
- }
-
- mRanger.snapSelection(target);
- notifySelectionChanged();
- }
-
- return true;
+ return position;
}
-
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 723700d..ae5644d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -52,7 +52,7 @@
public static final int TYPE_DOWNLOADS = 5;
public static final int TYPE_LOCAL = 6;
public static final int TYPE_MTP = 7;
- public static final int TYPE_CLOUD = 8;
+ public static final int TYPE_OTHER = 8;
public String authority;
public String rootId;
@@ -168,7 +168,10 @@
derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
// TODO: remove these special case icons
- if (isExternalStorage()) {
+ if (isHome()) {
+ derivedIcon = R.drawable.ic_root_home;
+ derivedType = TYPE_LOCAL;
+ } else if (isExternalStorage()) {
derivedIcon = R.drawable.ic_root_sdcard;
derivedType = TYPE_LOCAL;
} else if (isDownloads()) {
@@ -188,7 +191,7 @@
} else if (isMtp()) {
derivedType = TYPE_MTP;
} else {
- derivedType = TYPE_CLOUD;
+ derivedType = TYPE_OTHER;
}
}
@@ -196,6 +199,13 @@
return authority == null && rootId == null;
}
+ public boolean isHome() {
+ // Note that "home" is the expected root id for the auto-created
+ // user home directory on external storage. The "home" value should
+ // match ExternalStorageProvider.ROOT_ID_HOME.
+ return isExternalStorage() && "home".equals(rootId);
+ }
+
public boolean isExternalStorage() {
return "com.android.externalstorage.documents".equals(authority);
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 6f1a89b..369ab7d 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -32,6 +32,8 @@
import android.test.MoreAsserts;
import android.test.ServiceTestCase;
import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import com.android.documentsui.model.DocumentInfo;
@@ -52,6 +54,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+@MediumTest
public class CopyTest extends ServiceTestCase<CopyService> {
public CopyTest() {
@@ -89,9 +92,6 @@
super.tearDown();
}
- /**
- * Test copying a single file.
- */
public void testCopyFile() throws Exception {
String srcPath = "/test0.txt";
Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
@@ -131,9 +131,6 @@
MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
}
- /**
- * Test copying multiple files.
- */
public void testCopyMultipleFiles() throws Exception {
String testContent[] = {
"The five boxing wizards jump quickly",
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 9060516..71d8b34 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -31,11 +31,13 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.MotionEvent;
import com.android.documentsui.model.RootInfo;
+@LargeTest
public class FilesActivityUiTest extends InstrumentationTestCase {
private static final int TIMEOUT = 5000;
@@ -123,6 +125,7 @@
"Videos",
"Audio",
"Downloads",
+ "Home",
ROOT_0_ID,
ROOT_1_ID);
}
@@ -134,6 +137,13 @@
mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
}
+ public void testRootClickSetsWindowTitle() throws Exception {
+ initTestFiles();
+
+ mBot.openRoot("Home");
+ mBot.assertWindowTitle("Home");
+ }
+
public void testFilesList_LiveUpdate() throws Exception {
initTestFiles();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index 5c09794..ecad061 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static junit.framework.Assert.assertEquals;
+
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
@@ -80,6 +82,20 @@
mDevice.waitForIdle();
}
+ void assertWindowTitle(String expected) {
+ // Turns out the title field on a window does not have
+ // an id associated with it at runtime (which confuses the hell out of me)
+ // In code we address this via "android.R.id.title".
+ UiObject2 o = find(By.text(expected));
+ // It's a bit of a conceit that we then *assert* that the title
+ // is the value that we used to identify the UiObject2.
+ // If the preceeding lookup fails, this'll choke with an NPE.
+ // But given the issue described in the comment above, we're
+ // going to do it anyway. Because we shouldn't be looking up
+ // the uiobject by it's expected content :|
+ assertEquals(expected, o.getText());
+ }
+
void assertHasRoots(String... labels) throws UiObjectNotFoundException {
List<String> missing = new ArrayList<>();
for (String label : labels) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java
index 746e2117..b250e5d 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java
@@ -25,6 +25,7 @@
import android.support.v7.widget.RecyclerView;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.ViewGroup;
import com.android.documentsui.DirectoryResult;
@@ -34,6 +35,7 @@
import java.util.List;
+@SmallTest
public class DirectoryFragmentModelTest extends AndroidTestCase {
private static final int ITEM_COUNT = 5;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index 24f5c9e..b3d45ae 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -18,12 +18,12 @@
import android.support.v7.widget.RecyclerView;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
import com.android.documentsui.TestInputEvent;
-import com.android.documentsui.dirlist.MultiSelectManager;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import org.mockito.Mockito;
@@ -33,6 +33,7 @@
import java.util.List;
import java.util.Set;
+@SmallTest
public class MultiSelectManagerTest extends AndroidTestCase {
private static final List<String> items;
@@ -49,11 +50,13 @@
private MultiSelectManager mManager;
private TestAdapter mAdapter;
private TestCallback mCallback;
+ private TestSelectionEnvironment mEnv;
public void setUp() throws Exception {
mAdapter = new TestAdapter(items);
mCallback = new TestCallback();
- mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_MULTIPLE);
+ mEnv = new TestSelectionEnvironment();
+ mManager = new MultiSelectManager(mAdapter, mEnv, MultiSelectManager.MODE_MULTIPLE);
mManager.addCallback(mCallback);
}
@@ -162,7 +165,6 @@
assertRangeSelection(14, 17);
}
-
public void testSingleTapUp_ShiftReversesSelectionDirection() {
longPress(7);
shiftTap(17);
@@ -171,7 +173,7 @@
}
public void testSingleSelectMode() {
- mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_SINGLE);
+ mManager = new MultiSelectManager(mAdapter, mEnv, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
longPress(20);
tap(13);
@@ -179,13 +181,21 @@
}
public void testSingleSelectMode_ShiftTap() {
- mManager = new MultiSelectManager(mAdapter, MultiSelectManager.MODE_SINGLE);
+ mManager = new MultiSelectManager(mAdapter, mEnv, MultiSelectManager.MODE_SINGLE);
mManager.addCallback(mCallback);
longPress(13);
shiftTap(20);
assertSelection(20);
}
+ public void testSingleSelectMode_ShiftDoesNotExtendSelection() {
+ mManager = new MultiSelectManager(mAdapter, mEnv, MultiSelectManager.MODE_SINGLE);
+ mManager.addCallback(mCallback);
+ longPress(20);
+ keyToPosition(22, true);
+ assertSelection(22);
+ }
+
public void testProvisionalSelection() {
Selection s = mManager.getSelection();
assertSelection();
@@ -235,6 +245,10 @@
mManager.onSingleTapUp(TestInputEvent.shiftClick(position));
}
+ private void keyToPosition(int position, boolean shift) {
+ mManager.attemptChangePosition(position, shift);
+ }
+
private void assertSelected(int... expected) {
for (int i = 0; i < expected.length; i++) {
Selection selection = mManager.getSelection();
@@ -290,11 +304,8 @@
private static final class TestHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
- public View view;
- public String string;
public TestHolder(View view) {
super(view);
- this.view = view;
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
index c4b6ce5..c856b22 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
@@ -20,11 +20,13 @@
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.SparseBooleanArray;
import android.view.View;
import com.android.documentsui.dirlist.MultiSelectManager.GridModel;
+@SmallTest
public class MultiSelectManager_GridModelTest extends AndroidTestCase {
private static final int VIEW_PADDING_PX = 5;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java
index 64da750..72fc108 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java
@@ -17,10 +17,11 @@
package com.android.documentsui.dirlist;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
-
+@SmallTest
public class MultiSelectManager_SelectionTest extends AndroidTestCase{
private Selection selection;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java
new file mode 100644
index 0000000..b4324a8
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java
@@ -0,0 +1,108 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.view.View;
+
+import com.android.documentsui.dirlist.MultiSelectManager.SelectionEnvironment;
+
+public class TestSelectionEnvironment implements SelectionEnvironment {
+
+ @Override
+ public void showBand(Rect rect) {
+ }
+
+ @Override
+ public void hideBand() {
+ }
+
+ @Override
+ public void addOnScrollListener(OnScrollListener listener) {
+ }
+
+ @Override
+ public void removeOnScrollListener(OnScrollListener listener) {
+ }
+
+ @Override
+ public void scrollBy(int dy) {
+ }
+
+ @Override
+ public int getHeight() {
+ return 0;
+ }
+
+ @Override
+ public void invalidateView() {
+ }
+
+ @Override
+ public void runAtNextFrame(Runnable r) {
+ }
+
+ @Override
+ public void removeCallback(Runnable r) {
+ }
+
+ @Override
+ public Point createAbsolutePoint(Point relativePoint) {
+ return null;
+ }
+
+ @Override
+ public Rect getAbsoluteRectForChildViewAt(int index) {
+ return null;
+ }
+
+ @Override
+ public int getAdapterPositionAt(int index) {
+ return 0;
+ }
+
+ @Override
+ public int getAdapterPositionForChildView(View view) {
+ return 0;
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 0;
+ }
+
+ @Override
+ public int getRowCount() {
+ return 0;
+ }
+
+ @Override
+ public int getChildCount() {
+ return 0;
+ }
+
+ @Override
+ public int getVisibleChildCount() {
+ return 0;
+ }
+
+ @Override
+ public void focusItem(int position) {
+ }
+}
diff --git a/packages/ExternalStorageProvider/res/values-af/strings.xml b/packages/ExternalStorageProvider/res/values-af/strings.xml
index 1de881d..b5a159d 100644
--- a/packages/ExternalStorageProvider/res/values-af/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-af/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Eksterne berging"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Interne berging"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumente"</string>
+ <string name="root_home" msgid="7931555396767513359">"Tuis"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-am/strings.xml b/packages/ExternalStorageProvider/res/values-am/strings.xml
index 230fb06..f4f296d 100644
--- a/packages/ExternalStorageProvider/res/values-am/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-am/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ውጫዊ ማከማቻ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ውስጣዊ ማከማቻ"</string>
- <string name="root_documents" msgid="4051252304075469250">"ሰነዶች"</string>
+ <string name="root_home" msgid="7931555396767513359">"መነሻ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ar/strings.xml b/packages/ExternalStorageProvider/res/values-ar/strings.xml
index b20a056..4eee3e8 100644
--- a/packages/ExternalStorageProvider/res/values-ar/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ar/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"وحدة تخزين خارجية"</string>
<string name="root_internal_storage" msgid="827844243068584127">"وحدة تخزين داخلية"</string>
- <string name="root_documents" msgid="4051252304075469250">"مستندات"</string>
+ <string name="root_home" msgid="7931555396767513359">"الرئيسية"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-az-rAZ/strings.xml b/packages/ExternalStorageProvider/res/values-az-rAZ/strings.xml
index cd5ba2f..f7e5f8b 100644
--- a/packages/ExternalStorageProvider/res/values-az-rAZ/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-az-rAZ/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Xarici Yaddaş"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Daxili yaddaş"</string>
- <string name="root_documents" msgid="4051252304075469250">"Sənədlər"</string>
+ <string name="root_home" msgid="7931555396767513359">"Əsas səhifə"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-bg/strings.xml b/packages/ExternalStorageProvider/res/values-bg/strings.xml
index f5dce31..1cdf77b 100644
--- a/packages/ExternalStorageProvider/res/values-bg/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-bg/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Външно хранилище"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Вътрешно хранилище"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документи"</string>
+ <string name="root_home" msgid="7931555396767513359">"Начало"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-bn-rBD/strings.xml b/packages/ExternalStorageProvider/res/values-bn-rBD/strings.xml
index 3668065..842aed4 100644
--- a/packages/ExternalStorageProvider/res/values-bn-rBD/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-bn-rBD/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"বাহ্যিক সঞ্চয়স্থান"</string>
<string name="root_internal_storage" msgid="827844243068584127">"অভ্যন্তরীণ সঞ্চয়স্থান"</string>
- <string name="root_documents" msgid="4051252304075469250">"দস্তাবেজগুলি"</string>
+ <string name="root_home" msgid="7931555396767513359">"হোম"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ca/strings.xml b/packages/ExternalStorageProvider/res/values-ca/strings.xml
index 15e9d46..b3fd9f7 100644
--- a/packages/ExternalStorageProvider/res/values-ca/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ca/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Emmagatzematge extern"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Emmagatzematge intern"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Inici"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-cs/strings.xml b/packages/ExternalStorageProvider/res/values-cs/strings.xml
index b68a928..2eab596 100644
--- a/packages/ExternalStorageProvider/res/values-cs/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-cs/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externí úložiště"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Interní úložiště"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
+ <string name="root_home" msgid="7931555396767513359">"Výchozí adresář"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-da/strings.xml b/packages/ExternalStorageProvider/res/values-da/strings.xml
index dc565ae..d008f0e 100644
--- a/packages/ExternalStorageProvider/res/values-da/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-da/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Ekstern lagerplads"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Intern lagerplads"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenter"</string>
+ <string name="root_home" msgid="7931555396767513359">"Hjem"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-de/strings.xml b/packages/ExternalStorageProvider/res/values-de/strings.xml
index 318634a..50fc680 100644
--- a/packages/ExternalStorageProvider/res/values-de/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-de/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externer Speicher"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Interner Speicher"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumente"</string>
+ <string name="root_home" msgid="7931555396767513359">"Zuhause"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-el/strings.xml b/packages/ExternalStorageProvider/res/values-el/strings.xml
index b3aa792..9537afd 100644
--- a/packages/ExternalStorageProvider/res/values-el/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-el/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Εξωτερικός αποθηκευτικός χώρος"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Εσωτερικός αποθηκευτικός χώρος"</string>
- <string name="root_documents" msgid="4051252304075469250">"Έγγραφα"</string>
+ <string name="root_home" msgid="7931555396767513359">"Αρχική οθόνη"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-en-rAU/strings.xml b/packages/ExternalStorageProvider/res/values-en-rAU/strings.xml
index f88eb9e..be7aebc 100644
--- a/packages/ExternalStorageProvider/res/values-en-rAU/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-en-rAU/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"External Storage"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Internal storage"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-en-rGB/strings.xml b/packages/ExternalStorageProvider/res/values-en-rGB/strings.xml
index f88eb9e..be7aebc 100644
--- a/packages/ExternalStorageProvider/res/values-en-rGB/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-en-rGB/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"External Storage"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Internal storage"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-en-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-en-rIN/strings.xml
index f88eb9e..be7aebc 100644
--- a/packages/ExternalStorageProvider/res/values-en-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-en-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"External Storage"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Internal storage"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-es-rUS/strings.xml b/packages/ExternalStorageProvider/res/values-es-rUS/strings.xml
index e7e38b5..64a042d 100644
--- a/packages/ExternalStorageProvider/res/values-es-rUS/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-es-rUS/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Almacenamiento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Almacenamiento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Casa"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-es/strings.xml b/packages/ExternalStorageProvider/res/values-es/strings.xml
index e7e38b5..d59755e 100644
--- a/packages/ExternalStorageProvider/res/values-es/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-es/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Almacenamiento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Almacenamiento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Inicio"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-et-rEE/strings.xml b/packages/ExternalStorageProvider/res/values-et-rEE/strings.xml
index 6824e9d..7ea2caa 100644
--- a/packages/ExternalStorageProvider/res/values-et-rEE/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-et-rEE/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Väline talletusruum"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Sisemine salvestusruum"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumendid"</string>
+ <string name="root_home" msgid="7931555396767513359">"Kodu"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-eu-rES/strings.xml b/packages/ExternalStorageProvider/res/values-eu-rES/strings.xml
index 5881bf2..2f94acb 100644
--- a/packages/ExternalStorageProvider/res/values-eu-rES/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-eu-rES/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Kanpoko memoria"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Barneko memoria"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumentuak"</string>
+ <string name="root_home" msgid="7931555396767513359">"Etxea"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-fa/strings.xml b/packages/ExternalStorageProvider/res/values-fa/strings.xml
index 9ae8a47..c8c49a5 100644
--- a/packages/ExternalStorageProvider/res/values-fa/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-fa/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"حافظه خارجی"</string>
<string name="root_internal_storage" msgid="827844243068584127">"حافظهٔ داخلی"</string>
- <string name="root_documents" msgid="4051252304075469250">"اسناد"</string>
+ <string name="root_home" msgid="7931555396767513359">"صفحه اصلی"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-fi/strings.xml b/packages/ExternalStorageProvider/res/values-fi/strings.xml
index 9d1fbaa..660228a 100644
--- a/packages/ExternalStorageProvider/res/values-fi/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-fi/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Ulkoinen tallennustila"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Sisäinen tallennustila"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumentit"</string>
+ <string name="root_home" msgid="7931555396767513359">"Koti"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-fr-rCA/strings.xml b/packages/ExternalStorageProvider/res/values-fr-rCA/strings.xml
index b3fdd48..b94682f 100644
--- a/packages/ExternalStorageProvider/res/values-fr-rCA/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-fr-rCA/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Stockage externe"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Mémoire de stockage interne"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Accueil"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-fr/strings.xml b/packages/ExternalStorageProvider/res/values-fr/strings.xml
index b3fdd48..6a84bb4 100644
--- a/packages/ExternalStorageProvider/res/values-fr/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-fr/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Stockage externe"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Mémoire de stockage interne"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documents"</string>
+ <string name="root_home" msgid="7931555396767513359">"Répertoire de base"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-gl-rES/strings.xml b/packages/ExternalStorageProvider/res/values-gl-rES/strings.xml
index 780213f..d51eae9 100644
--- a/packages/ExternalStorageProvider/res/values-gl-rES/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-gl-rES/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Almacenamento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Almacenamento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Inicio"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-gu-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-gu-rIN/strings.xml
index ec8a0bd..3bcc72d 100644
--- a/packages/ExternalStorageProvider/res/values-gu-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-gu-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"બાહ્ય સંગ્રહ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"આંતરિક સંગ્રહ"</string>
- <string name="root_documents" msgid="4051252304075469250">"દસ્તાવેજો"</string>
+ <string name="root_home" msgid="7931555396767513359">"હોમ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-hi/strings.xml b/packages/ExternalStorageProvider/res/values-hi/strings.xml
index 8538081..93cc712 100644
--- a/packages/ExternalStorageProvider/res/values-hi/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-hi/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"बाहरी मेमोरी"</string>
<string name="root_internal_storage" msgid="827844243068584127">"मोबाइल मेमोरी"</string>
- <string name="root_documents" msgid="4051252304075469250">"दस्तावेज़"</string>
+ <string name="root_home" msgid="7931555396767513359">"होम"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-hr/strings.xml b/packages/ExternalStorageProvider/res/values-hr/strings.xml
index a74f8e8..c866351 100644
--- a/packages/ExternalStorageProvider/res/values-hr/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-hr/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Vanjska pohrana"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Unutarnja pohrana"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenti"</string>
+ <string name="root_home" msgid="7931555396767513359">"Početna"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-hu/strings.xml b/packages/ExternalStorageProvider/res/values-hu/strings.xml
index 3f72b41..db1c7db 100644
--- a/packages/ExternalStorageProvider/res/values-hu/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-hu/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Külső tárhely"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Belső tárhely"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumentumok"</string>
+ <string name="root_home" msgid="7931555396767513359">"Otthon"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-hy-rAM/strings.xml b/packages/ExternalStorageProvider/res/values-hy-rAM/strings.xml
index 5360124..0e1de49 100644
--- a/packages/ExternalStorageProvider/res/values-hy-rAM/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-hy-rAM/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Արտաքին պահոց"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Ներքին պահոց"</string>
- <string name="root_documents" msgid="4051252304075469250">"Փաստաթղթեր"</string>
+ <string name="root_home" msgid="7931555396767513359">"Գլխավոր էջ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-in/strings.xml b/packages/ExternalStorageProvider/res/values-in/strings.xml
index 42acde7..ca7f823 100644
--- a/packages/ExternalStorageProvider/res/values-in/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-in/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Penyimpanan Eksternal"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Penyimpanan internal"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumen"</string>
+ <string name="root_home" msgid="7931555396767513359">"Rumah"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-is-rIS/strings.xml b/packages/ExternalStorageProvider/res/values-is-rIS/strings.xml
index 0306165..ad04002 100644
--- a/packages/ExternalStorageProvider/res/values-is-rIS/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-is-rIS/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Ytri geymsla"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Innbyggð geymsla"</string>
- <string name="root_documents" msgid="4051252304075469250">"Skjöl"</string>
+ <string name="root_home" msgid="7931555396767513359">"Heim"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-it/strings.xml b/packages/ExternalStorageProvider/res/values-it/strings.xml
index 957b5ff..686ee1a 100644
--- a/packages/ExternalStorageProvider/res/values-it/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-it/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Archivio esterno"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Memoria interna"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documenti"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-iw/strings.xml b/packages/ExternalStorageProvider/res/values-iw/strings.xml
index 775506a..b45fb5c 100644
--- a/packages/ExternalStorageProvider/res/values-iw/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-iw/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"אחסון חיצוני"</string>
<string name="root_internal_storage" msgid="827844243068584127">"אחסון פנימי"</string>
- <string name="root_documents" msgid="4051252304075469250">"מסמכים"</string>
+ <string name="root_home" msgid="7931555396767513359">"דף הבית"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ja/strings.xml b/packages/ExternalStorageProvider/res/values-ja/strings.xml
index 188fca2..99baf16 100644
--- a/packages/ExternalStorageProvider/res/values-ja/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ja/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"外部ストレージ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"内部ストレージ"</string>
- <string name="root_documents" msgid="4051252304075469250">"ドキュメント"</string>
+ <string name="root_home" msgid="7931555396767513359">"自宅"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ka-rGE/strings.xml b/packages/ExternalStorageProvider/res/values-ka-rGE/strings.xml
index cc04860..c1bc5c7 100644
--- a/packages/ExternalStorageProvider/res/values-ka-rGE/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ka-rGE/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"გარე მეხსიერება"</string>
<string name="root_internal_storage" msgid="827844243068584127">"შიდა მეხსიერება"</string>
- <string name="root_documents" msgid="4051252304075469250">"დოკუმენტები"</string>
+ <string name="root_home" msgid="7931555396767513359">"მთავარი"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-kk-rKZ/strings.xml b/packages/ExternalStorageProvider/res/values-kk-rKZ/strings.xml
index ad49036..cf05782 100644
--- a/packages/ExternalStorageProvider/res/values-kk-rKZ/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-kk-rKZ/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Сыртқы жад"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Ішкі жад"</string>
- <string name="root_documents" msgid="4051252304075469250">"Құжаттар"</string>
+ <string name="root_home" msgid="7931555396767513359">"Негізгі бет"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-km-rKH/strings.xml b/packages/ExternalStorageProvider/res/values-km-rKH/strings.xml
index 9cf76d4..a2e926f 100644
--- a/packages/ExternalStorageProvider/res/values-km-rKH/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-km-rKH/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ឧបករណ៍ផ្ទុកខាងក្រៅ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string>
- <string name="root_documents" msgid="4051252304075469250">"ឯកសារ"</string>
+ <string name="root_home" msgid="7931555396767513359">"ដើម"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-kn-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-kn-rIN/strings.xml
index e32b1d3..1f0cfbf 100644
--- a/packages/ExternalStorageProvider/res/values-kn-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-kn-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ಆಂತರಿಕ ಸಂಗ್ರಹಣೆ"</string>
- <string name="root_documents" msgid="4051252304075469250">"ಡಾಕ್ಯುಮೆಂಟ್ಗಳು"</string>
+ <string name="root_home" msgid="7931555396767513359">"ಮುಖಪುಟ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ko/strings.xml b/packages/ExternalStorageProvider/res/values-ko/strings.xml
index 849d37e..365648d 100644
--- a/packages/ExternalStorageProvider/res/values-ko/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ko/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"외부 저장소"</string>
<string name="root_internal_storage" msgid="827844243068584127">"내부 저장소"</string>
- <string name="root_documents" msgid="4051252304075469250">"문서"</string>
+ <string name="root_home" msgid="7931555396767513359">"홈"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ky-rKG/strings.xml b/packages/ExternalStorageProvider/res/values-ky-rKG/strings.xml
index d3ccf7f1..4a0f211 100644
--- a/packages/ExternalStorageProvider/res/values-ky-rKG/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ky-rKG/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Тышкы сактагыч"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Ички сактагыч"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документтер"</string>
+ <string name="root_home" msgid="7931555396767513359">"Башкы бет"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-lo-rLA/strings.xml b/packages/ExternalStorageProvider/res/values-lo-rLA/strings.xml
index cecd9f5..9de6519 100644
--- a/packages/ExternalStorageProvider/res/values-lo-rLA/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-lo-rLA/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ບ່ອນຈັດເກັບຂໍ້ມູນພາຍນອກ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ບ່ອນຈັດເກັບຂໍ້ມູນພາຍໃນ"</string>
- <string name="root_documents" msgid="4051252304075469250">"ເອກະສານ"</string>
+ <string name="root_home" msgid="7931555396767513359">"ໜ້າຫຼັກ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-lt/strings.xml b/packages/ExternalStorageProvider/res/values-lt/strings.xml
index 240ea89..84ca2d4 100644
--- a/packages/ExternalStorageProvider/res/values-lt/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-lt/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Išorinė atmintinė"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Vidinė atmintinė"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumentai"</string>
+ <string name="root_home" msgid="7931555396767513359">"Pagrindinis katalogas"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-lv/strings.xml b/packages/ExternalStorageProvider/res/values-lv/strings.xml
index d308fe8..7eff0b9 100644
--- a/packages/ExternalStorageProvider/res/values-lv/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-lv/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Ārējā krātuve"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Iekšējā atmiņa"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenti"</string>
+ <string name="root_home" msgid="7931555396767513359">"Sākumdirektorijs"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-mk-rMK/strings.xml b/packages/ExternalStorageProvider/res/values-mk-rMK/strings.xml
index 8943d23..fe6b753 100644
--- a/packages/ExternalStorageProvider/res/values-mk-rMK/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-mk-rMK/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Надворешна меморија"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Внатрешна меморија"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документи"</string>
+ <string name="root_home" msgid="7931555396767513359">"Почетна страница"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
index 08e6dae..5369ec9 100644
--- a/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ബാഹ്യ സ്റ്റോറേജ്"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ആന്തരിക സ്റ്റോറേജ്"</string>
- <string name="root_documents" msgid="4051252304075469250">"പ്രമാണങ്ങൾ"</string>
+ <string name="root_home" msgid="7931555396767513359">"വീട്"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-mn-rMN/strings.xml b/packages/ExternalStorageProvider/res/values-mn-rMN/strings.xml
index 3d7b7f7..4604f32 100644
--- a/packages/ExternalStorageProvider/res/values-mn-rMN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-mn-rMN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Гадаад сан"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Дотоод сан"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документүүд"</string>
+ <string name="root_home" msgid="7931555396767513359">"Нүүр хуудас"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-mr-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-mr-rIN/strings.xml
index a7e7fbb..1310b0e 100644
--- a/packages/ExternalStorageProvider/res/values-mr-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-mr-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"बाह्य संचयन"</string>
<string name="root_internal_storage" msgid="827844243068584127">"अंतर्गत संचयन"</string>
- <string name="root_documents" msgid="4051252304075469250">"दस्तऐवज"</string>
+ <string name="root_home" msgid="7931555396767513359">"निवास"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ms-rMY/strings.xml b/packages/ExternalStorageProvider/res/values-ms-rMY/strings.xml
index cb4d736..007a1be 100644
--- a/packages/ExternalStorageProvider/res/values-ms-rMY/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ms-rMY/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Storan Luaran"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Storan dalaman"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumen"</string>
+ <string name="root_home" msgid="7931555396767513359">"Rumah"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-my-rMM/strings.xml b/packages/ExternalStorageProvider/res/values-my-rMM/strings.xml
index dc9d684..2df9a33 100644
--- a/packages/ExternalStorageProvider/res/values-my-rMM/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-my-rMM/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ပြင်ပသိုလှောင်ရာပစ္စည်း"</string>
<string name="root_internal_storage" msgid="827844243068584127">"စက်တွင်း သိုလှောင်ထားမှု"</string>
- <string name="root_documents" msgid="4051252304075469250">"စာရွက်စာတန်းများ"</string>
+ <string name="root_home" msgid="7931555396767513359">"ပင်မ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-nb/strings.xml b/packages/ExternalStorageProvider/res/values-nb/strings.xml
index a9ecb69..315d932 100644
--- a/packages/ExternalStorageProvider/res/values-nb/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-nb/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Ekstern lagring"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Intern lagring"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenter"</string>
+ <string name="root_home" msgid="7931555396767513359">"Hjem"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ne-rNP/strings.xml b/packages/ExternalStorageProvider/res/values-ne-rNP/strings.xml
index 5294043..4a9a8cd 100644
--- a/packages/ExternalStorageProvider/res/values-ne-rNP/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ne-rNP/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"बाह्य भण्डारण"</string>
<string name="root_internal_storage" msgid="827844243068584127">"आन्तरिक भण्डारण"</string>
- <string name="root_documents" msgid="4051252304075469250">"कागजातहरू"</string>
+ <string name="root_home" msgid="7931555396767513359">"गृह"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-nl/strings.xml b/packages/ExternalStorageProvider/res/values-nl/strings.xml
index bde6166..0ae88ce 100644
--- a/packages/ExternalStorageProvider/res/values-nl/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-nl/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externe opslag"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Interne opslag"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documenten"</string>
+ <string name="root_home" msgid="7931555396767513359">"Startscherm"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-pa-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-pa-rIN/strings.xml
index 0e91589..a805dd8 100644
--- a/packages/ExternalStorageProvider/res/values-pa-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-pa-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ਬਾਹਰੀ ਸਟੋਰੇਜ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ਅੰਦਰੂਨੀ ਸਟੋਰੇਜ"</string>
- <string name="root_documents" msgid="4051252304075469250">"ਦਸਤਾਵੇਜ਼"</string>
+ <string name="root_home" msgid="7931555396767513359">"ਘਰ"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-pl/strings.xml b/packages/ExternalStorageProvider/res/values-pl/strings.xml
index 6c5e7d7..66d83c7 100644
--- a/packages/ExternalStorageProvider/res/values-pl/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-pl/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Pamięć zewnętrzna"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Pamięć wewnętrzna"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
+ <string name="root_home" msgid="7931555396767513359">"Katalog domowy"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-pt-rBR/strings.xml b/packages/ExternalStorageProvider/res/values-pt-rBR/strings.xml
index 77c89b8..958eef4 100644
--- a/packages/ExternalStorageProvider/res/values-pt-rBR/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-pt-rBR/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Armazenamento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Armazenamento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Página inicial"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-pt-rPT/strings.xml b/packages/ExternalStorageProvider/res/values-pt-rPT/strings.xml
index 77c89b8..c8865e1 100644
--- a/packages/ExternalStorageProvider/res/values-pt-rPT/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-pt-rPT/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Armazenamento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Armazenamento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Casa"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-pt/strings.xml b/packages/ExternalStorageProvider/res/values-pt/strings.xml
index 77c89b8..958eef4 100644
--- a/packages/ExternalStorageProvider/res/values-pt/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-pt/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Armazenamento externo"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Armazenamento interno"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documentos"</string>
+ <string name="root_home" msgid="7931555396767513359">"Página inicial"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ro/strings.xml b/packages/ExternalStorageProvider/res/values-ro/strings.xml
index abd0b98..5bb4a7c 100644
--- a/packages/ExternalStorageProvider/res/values-ro/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ro/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Stocare externă"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Stocare internă"</string>
- <string name="root_documents" msgid="4051252304075469250">"Documente"</string>
+ <string name="root_home" msgid="7931555396767513359">"Director principal"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ru/strings.xml b/packages/ExternalStorageProvider/res/values-ru/strings.xml
index 740272f..c044a33 100644
--- a/packages/ExternalStorageProvider/res/values-ru/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ru/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Внешний накопитель"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Внутренний накопитель"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документы"</string>
+ <string name="root_home" msgid="7931555396767513359">"Главная"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-si-rLK/strings.xml b/packages/ExternalStorageProvider/res/values-si-rLK/strings.xml
index 15334bb..5292403 100644
--- a/packages/ExternalStorageProvider/res/values-si-rLK/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-si-rLK/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"බාහිර ආචයනය"</string>
<string name="root_internal_storage" msgid="827844243068584127">"අභ්යන්තර ආචයනය"</string>
- <string name="root_documents" msgid="4051252304075469250">"ලේඛන"</string>
+ <string name="root_home" msgid="7931555396767513359">"මුල් පිටුව"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sk/strings.xml b/packages/ExternalStorageProvider/res/values-sk/strings.xml
index 9be7b79..5157888 100644
--- a/packages/ExternalStorageProvider/res/values-sk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sk/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Externý ukladací priestor"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Interné úložisko"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
+ <string name="root_home" msgid="7931555396767513359">"Predvolený adresár"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sl/strings.xml b/packages/ExternalStorageProvider/res/values-sl/strings.xml
index 6ffa698..dd2cc24 100644
--- a/packages/ExternalStorageProvider/res/values-sl/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sl/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Zunanja shramba"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Notranja shramba"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumenti"</string>
+ <string name="root_home" msgid="7931555396767513359">"Korenska mapa"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sq-rAL/strings.xml b/packages/ExternalStorageProvider/res/values-sq-rAL/strings.xml
index dc346ea..3aafd1c 100644
--- a/packages/ExternalStorageProvider/res/values-sq-rAL/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sq-rAL/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Hapësirë e jashtme ruajtjeje"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Hapësira e brendshme ruajtëse"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokumente"</string>
+ <string name="root_home" msgid="7931555396767513359">"Kreu"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sr/strings.xml b/packages/ExternalStorageProvider/res/values-sr/strings.xml
index 54238a4..2d987ef 100644
--- a/packages/ExternalStorageProvider/res/values-sr/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sr/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Спољна меморија"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Интерна меморија"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документи"</string>
+ <string name="root_home" msgid="7931555396767513359">"Почетни"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sv/strings.xml b/packages/ExternalStorageProvider/res/values-sv/strings.xml
index 6eac11e..bc4788a 100644
--- a/packages/ExternalStorageProvider/res/values-sv/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sv/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Extern lagring"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Intern lagring"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokument"</string>
+ <string name="root_home" msgid="7931555396767513359">"Hem"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-sw/strings.xml b/packages/ExternalStorageProvider/res/values-sw/strings.xml
index 0d0e483..dcca92a 100644
--- a/packages/ExternalStorageProvider/res/values-sw/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sw/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Hifadhi ya Nje"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Hifadhi ya ndani"</string>
- <string name="root_documents" msgid="4051252304075469250">"Hati"</string>
+ <string name="root_home" msgid="7931555396767513359">"Mwanzo"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ta-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-ta-rIN/strings.xml
index d7bafbc..b859e7a 100644
--- a/packages/ExternalStorageProvider/res/values-ta-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ta-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"வெளிப்புறச் சேமிப்பிடம்"</string>
<string name="root_internal_storage" msgid="827844243068584127">"அகச் சேமிப்பிடம்"</string>
- <string name="root_documents" msgid="4051252304075469250">"ஆவணங்கள்"</string>
+ <string name="root_home" msgid="7931555396767513359">"முகப்பு"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-te-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-te-rIN/strings.xml
index 800d18e..934877e 100644
--- a/packages/ExternalStorageProvider/res/values-te-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-te-rIN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"బాహ్య నిల్వ"</string>
<string name="root_internal_storage" msgid="827844243068584127">"అంతర్గత నిల్వ"</string>
- <string name="root_documents" msgid="4051252304075469250">"పత్రాలు"</string>
+ <string name="root_home" msgid="7931555396767513359">"హోమ్"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-th/strings.xml b/packages/ExternalStorageProvider/res/values-th/strings.xml
index 796635e..957d6b7 100644
--- a/packages/ExternalStorageProvider/res/values-th/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-th/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"ที่จัดเก็บข้อมูลภายนอก"</string>
<string name="root_internal_storage" msgid="827844243068584127">"ที่จัดเก็บข้อมูลภายใน"</string>
- <string name="root_documents" msgid="4051252304075469250">"เอกสาร"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-tl/strings.xml b/packages/ExternalStorageProvider/res/values-tl/strings.xml
index 529cdc2..be7aebc 100644
--- a/packages/ExternalStorageProvider/res/values-tl/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-tl/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"External Storage"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Internal storage"</string>
- <string name="root_documents" msgid="4051252304075469250">"Mga Dokumento"</string>
+ <string name="root_home" msgid="7931555396767513359">"Home"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-tr/strings.xml b/packages/ExternalStorageProvider/res/values-tr/strings.xml
index d6bd52a..2ce1411 100644
--- a/packages/ExternalStorageProvider/res/values-tr/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-tr/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Harici Depolama"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Dahili depolama"</string>
- <string name="root_documents" msgid="4051252304075469250">"Dokümanlar"</string>
+ <string name="root_home" msgid="7931555396767513359">"Ev"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-uk/strings.xml b/packages/ExternalStorageProvider/res/values-uk/strings.xml
index b8206e0..0033bca 100644
--- a/packages/ExternalStorageProvider/res/values-uk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-uk/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Зовнішня пам’ять"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Внутрішня пам’ять"</string>
- <string name="root_documents" msgid="4051252304075469250">"Документи"</string>
+ <string name="root_home" msgid="7931555396767513359">"Головний екран"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-ur-rPK/strings.xml b/packages/ExternalStorageProvider/res/values-ur-rPK/strings.xml
index 02454bc..df46fb0 100644
--- a/packages/ExternalStorageProvider/res/values-ur-rPK/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ur-rPK/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"بیرونی اسٹوریج"</string>
<string name="root_internal_storage" msgid="827844243068584127">"داخلی اسٹوریج"</string>
- <string name="root_documents" msgid="4051252304075469250">"دستاویزات"</string>
+ <string name="root_home" msgid="7931555396767513359">"ہوم"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-uz-rUZ/strings.xml b/packages/ExternalStorageProvider/res/values-uz-rUZ/strings.xml
index 07cc14c..d1a956bf 100644
--- a/packages/ExternalStorageProvider/res/values-uz-rUZ/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-uz-rUZ/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Tashqi xotira"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Ichki xotira"</string>
- <string name="root_documents" msgid="4051252304075469250">"Hujjatlar"</string>
+ <string name="root_home" msgid="7931555396767513359">"Shaxsiy"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-vi/strings.xml b/packages/ExternalStorageProvider/res/values-vi/strings.xml
index b171c93..39e9c6c 100644
--- a/packages/ExternalStorageProvider/res/values-vi/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-vi/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Bộ nhớ ngoài"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Bộ nhớ trong"</string>
- <string name="root_documents" msgid="4051252304075469250">"Tài liệu"</string>
+ <string name="root_home" msgid="7931555396767513359">"Nhà riêng"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-zh-rCN/strings.xml b/packages/ExternalStorageProvider/res/values-zh-rCN/strings.xml
index 7df77dd..ea20dce 100644
--- a/packages/ExternalStorageProvider/res/values-zh-rCN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-zh-rCN/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"外部存储设备"</string>
<string name="root_internal_storage" msgid="827844243068584127">"内部存储空间"</string>
- <string name="root_documents" msgid="4051252304075469250">"文档"</string>
+ <string name="root_home" msgid="7931555396767513359">"主目录"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-zh-rHK/strings.xml b/packages/ExternalStorageProvider/res/values-zh-rHK/strings.xml
index 62d8afb..27f1f0a 100644
--- a/packages/ExternalStorageProvider/res/values-zh-rHK/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-zh-rHK/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"外部儲存空間"</string>
<string name="root_internal_storage" msgid="827844243068584127">"內部儲存空間"</string>
- <string name="root_documents" msgid="4051252304075469250">"文件"</string>
+ <string name="root_home" msgid="7931555396767513359">"主目錄"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-zh-rTW/strings.xml b/packages/ExternalStorageProvider/res/values-zh-rTW/strings.xml
index 62d8afb..b2d764a 100644
--- a/packages/ExternalStorageProvider/res/values-zh-rTW/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-zh-rTW/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"外部儲存空間"</string>
<string name="root_internal_storage" msgid="827844243068584127">"內部儲存空間"</string>
- <string name="root_documents" msgid="4051252304075469250">"文件"</string>
+ <string name="root_home" msgid="7931555396767513359">"主畫面"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values-zu/strings.xml b/packages/ExternalStorageProvider/res/values-zu/strings.xml
index 4a0a845..8a7c7df 100644
--- a/packages/ExternalStorageProvider/res/values-zu/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-zu/strings.xml
@@ -18,5 +18,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="7123375275748530234">"Isitoreji sangaphandle"</string>
<string name="root_internal_storage" msgid="827844243068584127">"Isitoreji sangaphakathi"</string>
- <string name="root_documents" msgid="4051252304075469250">"Amadokhumenti"</string>
+ <string name="root_home" msgid="7931555396767513359">"Ekhaya"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml
index f1c1ade..e48436e 100644
--- a/packages/ExternalStorageProvider/res/values/strings.xml
+++ b/packages/ExternalStorageProvider/res/values/strings.xml
@@ -20,6 +20,6 @@
<!-- Title for documents backend that offers internal storage. [CHAR LIMIT=24] -->
<string name="root_internal_storage">Internal storage</string>
- <!-- Title for documents backend that offers documents. [CHAR LIMIT=24] -->
- <string name="root_documents">Documents</string>
+ <!-- Title for user home dir. [CHAR LIMIT=24] -->
+ <string name="root_home">Home</string>
</resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index fcd45f2..2cedc72 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -85,9 +85,11 @@
public String docId;
public File visiblePath;
public File path;
+ public boolean reportAvailableBytes = true;
}
private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
+ private static final String ROOT_ID_HOME = "home";
private StorageManager mStorageManager;
private Handler mHandler;
@@ -118,6 +120,7 @@
private void updateVolumesLocked() {
mRoots.clear();
+ VolumeInfo primaryVolume = null;
final int userId = UserHandle.myUserId();
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (VolumeInfo volume : volumes) {
@@ -126,6 +129,9 @@
final String rootId;
final String title;
if (volume.getType() == VolumeInfo.TYPE_EMULATED) {
+ // save off the primary volume for subsequent "Home" dir initialization.
+ primaryVolume = volume;
+
// We currently only support a single emulated volume mounted at
// a time, and it's always considered the primary
rootId = ROOT_ID_PRIMARY_EMULATED;
@@ -152,25 +158,58 @@
continue;
}
+ final RootInfo root = new RootInfo();
+ mRoots.put(rootId, root);
+
+ root.rootId = rootId;
+ root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
+ | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
+
+ // Dunno when this would NOT be the case, but never hurts to be correct.
+ if (volume.isMountedWritable()) {
+ root.flags |= Root.FLAG_SUPPORTS_CREATE;
+ }
+ root.title = title;
+ if (volume.getType() == VolumeInfo.TYPE_PUBLIC) {
+ root.flags |= Root.FLAG_HAS_SETTINGS;
+ }
+ if (volume.isVisibleForRead(userId)) {
+ root.visiblePath = volume.getPathForUser(userId);
+ } else {
+ root.visiblePath = null;
+ }
+ root.path = volume.getInternalPathForUser(userId);
try {
- final RootInfo root = new RootInfo();
- mRoots.put(rootId, root);
-
- root.rootId = rootId;
- root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
- | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
- root.title = title;
- if (volume.getType() == VolumeInfo.TYPE_PUBLIC) {
- root.flags |= Root.FLAG_HAS_SETTINGS;
- }
- if (volume.isVisibleForRead(userId)) {
- root.visiblePath = volume.getPathForUser(userId);
- } else {
- root.visiblePath = null;
- }
- root.path = volume.getInternalPathForUser(userId);
root.docId = getDocIdForFile(root.path);
+ } catch (FileNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ // Finally, if primary storage is available we add the "Home" directory,
+ // creating it as needed.
+ if (primaryVolume != null && primaryVolume.isVisible()) {
+ final RootInfo root = new RootInfo();
+ root.rootId = ROOT_ID_HOME;
+ mRoots.put(root.rootId, root);
+ root.title = getContext().getString(R.string.root_home);
+
+ // Only report bytes on *volumes*...as a matter of policy.
+ root.reportAvailableBytes = false;
+ root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH
+ | Root.FLAG_SUPPORTS_IS_CHILD;
+
+ // Dunno when this would NOT be the case, but never hurts to be correct.
+ if (primaryVolume.isMountedWritable()) {
+ root.flags |= Root.FLAG_SUPPORTS_CREATE;
+ }
+
+ root.visiblePath = new File(
+ primaryVolume.getPathForUser(userId), root.rootId);
+ root.path = new File(
+ primaryVolume.getInternalPathForUser(userId), root.rootId);
+ try {
+ root.docId = getDocIdForFile(root.path);
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
@@ -312,7 +351,8 @@
row.add(Root.COLUMN_FLAGS, root.flags);
row.add(Root.COLUMN_TITLE, root.title);
row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
- row.add(Root.COLUMN_AVAILABLE_BYTES, root.path.getFreeSpace());
+ row.add(Root.COLUMN_AVAILABLE_BYTES,
+ root.reportAvailableBytes ? root.path.getFreeSpace() : -1);
}
}
return result;
diff --git a/packages/FakeOemFeatures/AndroidManifest.xml b/packages/FakeOemFeatures/AndroidManifest.xml
index 93b8b47..fe74ad8 100644
--- a/packages/FakeOemFeatures/AndroidManifest.xml
+++ b/packages/FakeOemFeatures/AndroidManifest.xml
@@ -11,7 +11,9 @@
android:allowClearUserData="false"
android:allowBackup="false"
android:hardwareAccelerated="true"
- android:label="Fake OEM Features">
+ android:label="Fake OEM Features"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".FakeCoreService" android:process=":core"
android:label="Fake OEM Core Service" />
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 6a4d4bf..ed84c0d 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -28,7 +28,9 @@
<application
android:label="@string/app_label"
- android:process="system">
+ android:process="system"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<uses-library android:name="com.android.location.provider" />
@@ -37,7 +39,7 @@
version. -->
<service android:name="com.android.location.fused.FusedLocationService"
android:exported="true"
- android:permission="android.permission.WRITE_SECURE_SETTINGS" >
+ android:permission="android.permission.WRITE_SECURE_SETTINGS">
<intent-filter>
<action android:name="com.android.location.service.FusedLocationProvider" />
</intent-filter>
diff --git a/packages/InputDevices/AndroidManifest.xml b/packages/InputDevices/AndroidManifest.xml
index f0e4abc..07885ea 100644
--- a/packages/InputDevices/AndroidManifest.xml
+++ b/packages/InputDevices/AndroidManifest.xml
@@ -6,7 +6,9 @@
<application
android:allowClearUserData="false"
android:label="@string/app_label"
- android:process="system">
+ android:process="system"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<receiver android:name=".InputDeviceReceiver"
android:label="@string/keyboard_layouts_label">
diff --git a/packages/Keyguard/AndroidManifest.xml b/packages/Keyguard/AndroidManifest.xml
index e19246c..54972b4 100644
--- a/packages/Keyguard/AndroidManifest.xml
+++ b/packages/Keyguard/AndroidManifest.xml
@@ -45,7 +45,9 @@
<application android:label="@string/app_name"
android:process="com.android.systemui"
android:persistent="true"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
</application>
</manifest>
diff --git a/packages/Keyguard/res/values-ja/strings.xml b/packages/Keyguard/res/values-ja/strings.xml
index cea4319..3230beb 100644
--- a/packages/Keyguard/res/values-ja/strings.xml
+++ b/packages/Keyguard/res/values-ja/strings.xml
@@ -110,8 +110,8 @@
<string name="keyguard_carrier_default" msgid="8700650403054042153">"通信サービスはありません。"</string>
<string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string>
<string name="airplane_mode" msgid="3122107900897202805">"機内モード"</string>
- <string name="kg_prompt_reason_restart_pattern" msgid="489430505491862444">"端末を再起動するにはパターンが必要です。"</string>
- <string name="kg_prompt_reason_restart_pin" msgid="994878216570694974">"端末を再起動するにはPINが必要です。"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="489430505491862444">"端末を再起動した時にはパターンが必要です。"</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="994878216570694974">"端末を再起動した時にはPINが必要です。"</string>
<string name="kg_prompt_reason_restart_password" msgid="2375742919528461664">"端末を再起動した時にはパスワードが必要です。"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="8930047492617900785">"セキュリティを強化するため、パターンが必要です。"</string>
<string name="kg_prompt_reason_timeout_pin" msgid="7470468607947726377">"セキュリティを強化するため、PINが必要です。"</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 5b1e45d..c7b217c 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -92,8 +92,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁平板电脑。系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"您已经 <xliff:g id="NUMBER">%d</xliff:g> 次错误地尝试解锁手机。系统将移除此工作资料,这会删除所有的工作资料数据。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁平板电脑。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已连续 <xliff:g id="NUMBER_0">%d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"SIM卡PIN码不正确,您现在必须联系运营商为您解锁设备。"</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
<item quantity="other">SIM 卡 PIN 码不正确,您还可尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index f95b0ae..9d1df26 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -198,12 +197,18 @@
}
private String getOwnerInfo() {
- ContentResolver res = getContext().getContentResolver();
String info = null;
- final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
- KeyguardUpdateMonitor.getCurrentUser());
- if (ownerInfoEnabled) {
- info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) {
+ // Use the device owner information set by device policy client via
+ // device policy manager.
+ info = mLockPatternUtils.getDeviceOwnerInfo();
+ } else {
+ // Use the current user owner information if enabled.
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (ownerInfoEnabled) {
+ info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ }
}
return info;
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 0d4265a..1c96906 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -18,7 +18,7 @@
import android.content.ContentResolver;
import android.database.Cursor;
-import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -26,6 +26,7 @@
import android.provider.DocumentsContract;
import android.util.Log;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
@@ -44,12 +45,14 @@
private final MtpManager mMtpManager;
private final ContentResolver mResolver;
+ private final MtpDatabase mDatabase;
private final TaskList mTaskList = new TaskList();
private boolean mHasBackgroundThread = false;
- DocumentLoader(MtpManager mtpManager, ContentResolver resolver) {
+ DocumentLoader(MtpManager mtpManager, ContentResolver resolver, MtpDatabase database) {
mMtpManager = mtpManager;
mResolver = resolver;
+ mDatabase = database;
}
private static MtpObjectInfo[] loadDocuments(MtpManager manager, int deviceId, int[] handles)
@@ -65,13 +68,17 @@
throws IOException {
LoaderTask task = mTaskList.findTask(parent);
if (task == null) {
+ if (parent.mDocumentId == null) {
+ throw new FileNotFoundException("Parent not found.");
+ }
+
int parentHandle = parent.mObjectHandle;
// Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to
// getObjectHandles if we would like to obtain children under the root.
if (parentHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
}
- task = new LoaderTask(parent, mMtpManager.getObjectHandles(
+ task = new LoaderTask(mDatabase, parent, mMtpManager.getObjectHandles(
parent.mDeviceId, parent.mStorageId, parentHandle));
task.fillDocuments(loadDocuments(
mMtpManager,
@@ -83,11 +90,10 @@
}
mTaskList.addFirst(task);
- if (!task.completed() && !mHasBackgroundThread) {
+ if (task.getState() == LoaderTask.STATE_LOADING && !mHasBackgroundThread) {
mHasBackgroundThread = true;
new BackgroundLoaderThread().start();
}
-
return task.createCursor(mResolver, columnNames);
}
@@ -120,26 +126,20 @@
deviceId = task.mIdentifier.mDeviceId;
handles = task.getUnloadedObjectHandles(NUM_LOADING_ENTRIES);
}
- MtpObjectInfo[] objectInfos;
+
try {
- objectInfos = loadDocuments(mMtpManager, deviceId, handles);
- } catch (IOException exception) {
- objectInfos = null;
- Log.d(MtpDocumentsProvider.TAG, exception.getMessage());
- }
- synchronized (DocumentLoader.this) {
- if (objectInfos != null) {
- task.fillDocuments(objectInfos);
- final boolean shouldNotify =
- task.mLastNotified.getTime() <
- new Date().getTime() - NOTIFY_PERIOD_MS ||
- task.completed();
- if (shouldNotify) {
- task.notify(mResolver);
- }
- } else {
- mTaskList.remove(task);
+ final MtpObjectInfo[] objectInfos =
+ loadDocuments(mMtpManager, deviceId, handles);
+ task.fillDocuments(objectInfos);
+ final boolean shouldNotify =
+ task.mLastNotified.getTime() <
+ new Date().getTime() - NOTIFY_PERIOD_MS ||
+ task.getState() != LoaderTask.STATE_LOADING;
+ if (shouldNotify) {
+ task.notify(mResolver);
}
+ } catch (IOException exception) {
+ task.setError(exception);
}
}
}
@@ -156,7 +156,7 @@
LoaderTask findRunningTask() {
for (int i = 0; i < size(); i++) {
- if (!get(i).completed())
+ if (get(i).getState() == LoaderTask.STATE_LOADING)
return get(i);
}
return null;
@@ -165,7 +165,7 @@
void clearCompletedTasks() {
int i = 0;
while (i < size()) {
- if (get(i).completed()) {
+ if (get(i).getState() == LoaderTask.STATE_COMPLETED) {
remove(i);
} else {
i++;
@@ -186,36 +186,51 @@
}
private static class LoaderTask {
+ static final int STATE_LOADING = 0;
+ static final int STATE_COMPLETED = 1;
+ static final int STATE_ERROR = 2;
+
+ final MtpDatabase mDatabase;
final Identifier mIdentifier;
final int[] mObjectHandles;
- final MtpObjectInfo[] mObjectInfos;
Date mLastNotified;
int mNumLoaded;
+ Exception mError;
- LoaderTask(Identifier identifier, int[] objectHandles) {
+ LoaderTask(MtpDatabase database, Identifier identifier, int[] objectHandles) {
+ mDatabase = database;
mIdentifier = identifier;
mObjectHandles = objectHandles;
- mObjectInfos = new MtpObjectInfo[mObjectHandles.length];
mNumLoaded = 0;
mLastNotified = new Date();
}
- Cursor createCursor(ContentResolver resolver, String[] columnNames) {
- final MatrixCursor cursor = new MatrixCursor(columnNames);
- final Identifier rootIdentifier = new Identifier(
- mIdentifier.mDeviceId, mIdentifier.mStorageId);
- for (int i = 0; i < mNumLoaded; i++) {
- CursorHelper.addToCursor(mObjectInfos[i], rootIdentifier, cursor.newRow());
- }
+ Cursor createCursor(ContentResolver resolver, String[] columnNames) throws IOException {
final Bundle extras = new Bundle();
- extras.putBoolean(DocumentsContract.EXTRA_LOADING, !completed());
+ switch (getState()) {
+ case STATE_LOADING:
+ extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
+ break;
+ case STATE_ERROR:
+ throw new IOException(mError);
+ }
+
+ final Cursor cursor = mDatabase.queryChildDocuments(
+ columnNames, mIdentifier.mDocumentId, /* use old ID format */ true);
cursor.setNotificationUri(resolver, createUri());
cursor.respond(extras);
+
return cursor;
}
- boolean completed() {
- return mNumLoaded == mObjectInfos.length;
+ int getState() {
+ if (mError != null) {
+ return STATE_ERROR;
+ } else if (mNumLoaded == mObjectHandles.length) {
+ return STATE_COMPLETED;
+ } else {
+ return STATE_LOADING;
+ }
}
int[] getUnloadedObjectHandles(int count) {
@@ -230,9 +245,32 @@
mLastNotified = new Date();
}
- void fillDocuments(MtpObjectInfo[] objectInfos) {
- for (int i = 0; i < objectInfos.length; i++) {
- mObjectInfos[mNumLoaded++] = objectInfos[i];
+ void fillDocuments(MtpObjectInfo[] objectInfoList) {
+ if (objectInfoList.length == 0 || getState() != STATE_LOADING) {
+ return;
+ }
+ if (mNumLoaded == 0) {
+ mDatabase.startAddingChildDocuments(mIdentifier.mDocumentId);
+ }
+ try {
+ mDatabase.putChildDocuments(
+ mIdentifier.mDeviceId, mIdentifier.mDocumentId, objectInfoList);
+ mNumLoaded += objectInfoList.length;
+ } catch (SQLiteException exp) {
+ mError = exp;
+ mNumLoaded = 0;
+ }
+ if (getState() != STATE_LOADING) {
+ mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId);
+ }
+ }
+
+ void setError(Exception message) {
+ final int lastState = getState();
+ mError = message;
+ mNumLoaded = 0;
+ if (lastState == STATE_LOADING) {
+ mDatabase.stopAddingChildDocuments(mIdentifier.mDocumentId);
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
index ae29f52..4238721e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Identifier.java
@@ -23,6 +23,7 @@
final int mDeviceId;
final int mStorageId;
final int mObjectHandle;
+ final String mDocumentId;
static Identifier createFromRootId(String rootId) {
final String[] components = rootId.split("_");
@@ -45,9 +46,14 @@
}
Identifier(int deviceId, int storageId, int objectHandle) {
+ this(deviceId, storageId, objectHandle, null);
+ }
+
+ Identifier(int deviceId, int storageId, int objectHandle, String documentId) {
mDeviceId = deviceId;
mStorageId = storageId;
mObjectHandle = objectHandle;
+ mDocumentId = documentId;
}
// TODO: Make the ID persistent.
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 0f31e2c..3dc69cc 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -16,22 +16,21 @@
package com.android.mtp;
+import static com.android.mtp.MtpDatabaseConstants.*;
+
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
import android.mtp.MtpObjectInfo;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.Objects;
+import java.util.HashMap;
+import java.util.Map;
/**
* Database for MTP objects.
@@ -46,188 +45,159 @@
* remembers the map of document ID and object handle, and remaps new object handle with document ID
* by comparing the directory structure and object name.
*
+ * To start putting documents into the database, the client needs to call
+ * {@link #startAddingChildDocuments(String)} with the parent document ID. Also it needs to call
+ * {@link #stopAddingChildDocuments(String)} after putting all child documents to the database.
+ * (All explanations are same for root documents)
+ *
+ * database.startAddingChildDocuments();
+ * database.putChildDocuments();
+ * database.stopAddingChildDocuments();
+ *
+ * To update the existing documents, the client code can repeat to call the three methods again.
+ * The newly added rows update corresponding existing rows that have same MTP identifier like
+ * objectHandle.
+ *
+ * The client can call putChildDocuments multiple times to add documents by chunk, but it needs to
+ * put all documents under the parent before calling stopAddingChildDocuments. Otherwise missing
+ * documents are regarded as deleted, and will be removed from the database.
+ *
+ * If the client calls clearMtpIdentifier(), it clears MTP identifier in the database. In this case,
+ * the database tries to find corresponding rows by using document's name instead of MTP identifier
+ * at the next update cycle.
+ *
* TODO: Remove @VisibleForTesting annotation when we start to use this class.
* TODO: Improve performance by SQL optimization.
*/
@VisibleForTesting
class MtpDatabase {
- private static final int VERSION = 1;
- private static final String NAME = "mtp";
+ private final MtpDatabaseInternal mDatabase;
/**
- * Table representing documents including root documents.
+ * Mapping mode for roots/documents where we start adding child documents.
+ * Methods operate the state needs to be synchronized.
*/
- private static final String TABLE_DOCUMENTS = "Documents";
-
- /**
- * Table containing additional information only available for root documents.
- * The table uses same primary keys with corresponding documents.
- */
- private static final String TABLE_ROOT_EXTRA = "RootExtra";
-
- /**
- * View to join Documents and RootExtra tables to provide roots information.
- */
- private static final String VIEW_ROOTS = "Roots";
-
- static final String COLUMN_DEVICE_ID = "device_id";
- static final String COLUMN_STORAGE_ID = "storage_id";
- static final String COLUMN_OBJECT_HANDLE = "object_handle";
- static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
- static final String COLUMN_ROW_STATE = "row_state";
-
- /**
- * The state represents that the row has a valid object handle.
- */
- static final int ROW_STATE_MAPPED = 0;
-
- /**
- * The state represents that the object handle was cleared because the MTP session closed.
- * External application can still fetch the unmapped documents. If the external application
- * tries to open an unmapped document, the provider resolves the document with new object handle
- * ahead.
- */
- static final int ROW_STATE_UNMAPPED = 1;
-
- /**
- * The state represents the raw has a valid object handle but it may be going to be merged into
- * another unmapped row. After fetching all documents under the parent, the database tries to
- * map the mapping document and the unmapped document in order to keep old document ID alive.
- */
- static final int ROW_STATE_MAPPING = 2;
-
- private static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
- private static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
- private static final String SELECTION_ROOT_DOCUMENTS =
- COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
- private static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?";
-
- static class ParentNotFoundException extends Exception {}
-
- private static class OpenHelper extends SQLiteOpenHelper {
- private static final String QUERY_CREATE_DOCUMENTS =
- "CREATE TABLE " + TABLE_DOCUMENTS + " (" +
- Document.COLUMN_DOCUMENT_ID +
- " INTEGER PRIMARY KEY AUTOINCREMENT," +
- COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
- COLUMN_STORAGE_ID + " INTEGER," +
- COLUMN_OBJECT_HANDLE + " INTEGER," +
- COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
- COLUMN_ROW_STATE + " INTEGER NOT NULL," +
- Document.COLUMN_MIME_TYPE + " TEXT," +
- Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
- Document.COLUMN_SUMMARY + " TEXT," +
- Document.COLUMN_LAST_MODIFIED + " INTEGER," +
- Document.COLUMN_ICON + " INTEGER," +
- Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
- Document.COLUMN_SIZE + " INTEGER NOT NULL);";
-
- private static final String QUERY_CREATE_ROOT_EXTRA =
- "CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
- Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
- Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
- Root.COLUMN_AVAILABLE_BYTES + " INTEGER NOT NULL," +
- Root.COLUMN_CAPACITY_BYTES + " INTEGER NOT NULL," +
- Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
-
- /**
- * Creates a view to join Documents table and RootExtra table on their primary keys to
- * provide DocumentContract.Root equivalent information.
- */
- private static final String QUERY_CREATE_VIEW_ROOTS =
- "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " +
- TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
- Root.COLUMN_ROOT_ID + "," +
- TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," +
- TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " +
- Root.COLUMN_ICON + "," +
- TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " +
- Root.COLUMN_TITLE + "," +
- TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
- Root.COLUMN_SUMMARY + "," +
- TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
- Root.COLUMN_DOCUMENT_ID + "," +
- TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
- TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
- TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," +
- TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE +
- " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA +
- " ON " +
- COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " +
- TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
- "=" +
- TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID;
-
- public OpenHelper(Context context) {
- super(context, NAME, null, VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL(QUERY_CREATE_DOCUMENTS);
- db.execSQL(QUERY_CREATE_ROOT_EXTRA);
- db.execSQL(QUERY_CREATE_VIEW_ROOTS);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- throw new UnsupportedOperationException();
- }
- }
-
- private final SQLiteDatabase mDatabase;
+ private final Map<String, Integer> mMappingMode = new HashMap<>();
@VisibleForTesting
- MtpDatabase(Context context) {
- final OpenHelper helper = new OpenHelper(context);
- mDatabase = helper.getWritableDatabase();
+ MtpDatabase(Context context, int flags) {
+ mDatabase = new MtpDatabaseInternal(context, flags);
}
+ /**
+ * Closes the database.
+ */
@VisibleForTesting
- static void deleteDatabase(Context context) {
- SQLiteDatabase.deleteDatabase(context.getDatabasePath(NAME));
+ void close() {
+ mDatabase.close();
}
- @VisibleForTesting
+ /**
+ * {@link MtpDatabaseInternal#queryRoots}
+ */
Cursor queryRoots(String[] columnNames) {
- return mDatabase.query(
- VIEW_ROOTS,
- columnNames,
- COLUMN_ROW_STATE + " IN (?, ?)",
- strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED),
- null,
- null,
- null);
+ return mDatabase.queryRoots(columnNames);
}
+ /**
+ * {@link MtpDatabaseInternal#queryRootDocuments}
+ */
@VisibleForTesting
Cursor queryRootDocuments(String[] columnNames) {
- return mDatabase.query(
- TABLE_DOCUMENTS,
- columnNames,
- COLUMN_ROW_STATE + " IN (?, ?)",
- strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED),
- null,
- null,
- null);
+ return mDatabase.queryRootDocuments(columnNames);
}
+ /**
+ * {@link MtpDatabaseInternal#queryChildDocuments}
+ */
@VisibleForTesting
Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
- return mDatabase.query(
- TABLE_DOCUMENTS,
- columnNames,
- COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_PARENT_DOCUMENT_ID + " = ?",
- strings(ROW_STATE_MAPPED, ROW_STATE_UNMAPPED, parentDocumentId),
- null,
- null,
- null);
+ return queryChildDocuments(columnNames, parentDocumentId, false);
}
@VisibleForTesting
- void putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
+ Cursor queryChildDocuments(String[] columnNames, String parentDocumentId, boolean useOldId) {
+ final String[] newColumnNames = new String[columnNames.length];
+
+ // TODO: Temporary replace document ID with old format.
+ for (int i = 0; i < columnNames.length; i++) {
+ if (useOldId && DocumentsContract.Document.COLUMN_DOCUMENT_ID.equals(columnNames[i])) {
+ newColumnNames[i] = COLUMN_DEVICE_ID + " || '_' || " + COLUMN_STORAGE_ID +
+ " || '_' || IFNULL(" + COLUMN_OBJECT_HANDLE + ",0) AS " +
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID;
+ } else {
+ newColumnNames[i] = columnNames[i];
+ }
+ }
+
+ return mDatabase.queryChildDocuments(newColumnNames, parentDocumentId);
+ }
+
+ Identifier createIdentifier(String parentDocumentId) {
+ return mDatabase.createIdentifier(parentDocumentId);
+ }
+
+ /**
+ * {@link MtpDatabaseInternal#removeDeviceRows}
+ */
+ void removeDeviceRows(int deviceId) {
+ mDatabase.removeDeviceRows(deviceId);
+ }
+
+ /**
+ * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for root documents.
+ * @param deviceId Device ID.
+ */
+ synchronized void startAddingRootDocuments(int deviceId) {
+ final String mappingStateKey = getRootDocumentsMappingStateKey(deviceId);
+ if (mMappingMode.containsKey(mappingStateKey)) {
+ throw new Error("Mapping for the root has already started.");
+ }
+ mMappingMode.put(
+ mappingStateKey,
+ mDatabase.startAddingDocuments(
+ SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)));
+ }
+
+ /**
+ * Invokes {@link MtpDatabaseInternal#startAddingDocuments} for child of specific documents.
+ * @param parentDocumentId Document ID for parent document.
+ */
+ @VisibleForTesting
+ synchronized void startAddingChildDocuments(String parentDocumentId) {
+ final String mappingStateKey = getChildDocumentsMappingStateKey(parentDocumentId);
+ if (mMappingMode.containsKey(mappingStateKey)) {
+ throw new Error("Mapping for the root has already started.");
+ }
+ mMappingMode.put(
+ mappingStateKey,
+ mDatabase.startAddingDocuments(SELECTION_CHILD_DOCUMENTS, parentDocumentId));
+ }
+
+ /**
+ * Puts root information to database.
+ * @param deviceId Device ID
+ * @param resources Resources required to localize root name.
+ * @param roots List of root information.
+ * @return If roots are added or removed from the database.
+ */
+ synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
mDatabase.beginTransaction();
try {
+ final boolean heuristic;
+ final String mapColumn;
+ switch (mMappingMode.get(getRootDocumentsMappingStateKey(deviceId))) {
+ case MAP_BY_MTP_IDENTIFIER:
+ heuristic = false;
+ mapColumn = COLUMN_STORAGE_ID;
+ break;
+ case MAP_BY_NAME:
+ heuristic = true;
+ mapColumn = Document.COLUMN_DISPLAY_NAME;
+ break;
+ default:
+ throw new Error("Unexpected map mode.");
+ }
final ContentValues[] valuesList = new ContentValues[roots.length];
for (int i = 0; i < roots.length; i++) {
if (roots[i].mDeviceId != deviceId) {
@@ -236,200 +206,127 @@
valuesList[i] = new ContentValues();
getRootDocumentValues(valuesList[i], resources, roots[i]);
}
- final long[] documentIds =
- putDocuments(valuesList, SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
+ final boolean changed = mDatabase.putDocuments(
+ valuesList,
+ SELECTION_ROOT_DOCUMENTS,
+ Integer.toString(deviceId),
+ heuristic,
+ mapColumn);
final ContentValues values = new ContentValues();
int i = 0;
for (final MtpRoot root : roots) {
// Use the same value for the root ID and the corresponding document ID.
- values.put(Root.COLUMN_ROOT_ID, documentIds[i++]);
- values.put(Root.COLUMN_FLAGS,
- Root.FLAG_SUPPORTS_IS_CHILD |
- Root.FLAG_SUPPORTS_CREATE);
+ final String documentId = valuesList[i++].getAsString(Document.COLUMN_DOCUMENT_ID);
+ // If it fails to insert/update documents, the document ID will be set with -1.
+ // In this case we don't insert/update root extra information neither.
+ if (documentId == null) {
+ continue;
+ }
+ values.put(Root.COLUMN_ROOT_ID, documentId);
+ values.put(
+ Root.COLUMN_FLAGS,
+ Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
values.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace);
values.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity);
values.put(Root.COLUMN_MIME_TYPES, "");
- mDatabase.insert(TABLE_ROOT_EXTRA, null, values);
+ mDatabase.putRootExtra(values);
}
mDatabase.setTransactionSuccessful();
+ return changed;
} finally {
mDatabase.endTransaction();
}
}
+ /**
+ * Puts document information to database.
+ * @param deviceId Device ID
+ * @param parentId Parent document ID.
+ * @param documents List of document information.
+ */
@VisibleForTesting
- void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
+ synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
+ final boolean heuristic;
+ final String mapColumn;
+ switch (mMappingMode.get(getChildDocumentsMappingStateKey(parentId))) {
+ case MAP_BY_MTP_IDENTIFIER:
+ heuristic = false;
+ mapColumn = COLUMN_OBJECT_HANDLE;
+ break;
+ case MAP_BY_NAME:
+ heuristic = true;
+ mapColumn = Document.COLUMN_DISPLAY_NAME;
+ break;
+ default:
+ throw new Error("Unexpected map mode.");
+ }
final ContentValues[] valuesList = new ContentValues[documents.length];
for (int i = 0; i < documents.length; i++) {
valuesList[i] = new ContentValues();
getChildDocumentValues(valuesList[i], deviceId, parentId, documents[i]);
}
- putDocuments(valuesList, SELECTION_CHILD_DOCUMENTS, parentId);
+ mDatabase.putDocuments(
+ valuesList, SELECTION_CHILD_DOCUMENTS, parentId, heuristic, mapColumn);
}
/**
- * Clears MTP related identifier.
- * It clears MTP's object handle and storage ID that are not stable over MTP sessions and mark
- * the all documents as 'unmapped'. It also remove 'mapping' rows as mapping is cancelled now.
+ * Clears mapping between MTP identifier and document/root ID.
*/
@VisibleForTesting
- void clearMapping() {
- mDatabase.beginTransaction();
- try {
- deleteDocumentsAndRoots(COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_MAPPING));
- final ContentValues values = new ContentValues();
- values.putNull(COLUMN_OBJECT_HANDLE);
- values.putNull(COLUMN_STORAGE_ID);
- values.put(COLUMN_ROW_STATE, ROW_STATE_UNMAPPED);
- mDatabase.update(TABLE_DOCUMENTS, values, null, null);
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
- }
- }
-
- @VisibleForTesting
- void resolveRootDocuments(int deviceId) {
- resolveDocuments(SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId));
- }
-
- @VisibleForTesting
- void resolveChildDocuments(String parentId) {
- resolveDocuments(SELECTION_CHILD_DOCUMENTS, parentId);
+ synchronized void clearMapping() {
+ mDatabase.clearMapping();
+ mMappingMode.clear();
}
/**
- * Puts the documents into the database.
- * If the database found another unmapped document that shares the same name and parent,
- * the document may be merged into the unmapped document. In that case, the database marks the
- * root as 'mapping' and wait for {@link #resolveRootDocuments(int)} is invoked.
- * @param valuesList Values that are stored in the database.
- * @param selection SQL where closure to select rows that shares the same parent.
- * @param arg Argument for selection SQL.
- * @return List of Document ID inserted to the table.
+ * Stops adding root documents.
+ * @param deviceId Device ID.
+ * @return True if new rows are added/removed.
*/
- private long[] putDocuments(ContentValues[] valuesList, String selection, String arg) {
- mDatabase.beginTransaction();
- try {
- final long[] documentIds = new long[valuesList.length];
- int i = 0;
- for (final ContentValues values : valuesList) {
- final String displayName =
- values.getAsString(Document.COLUMN_DISPLAY_NAME);
- final long numUnmapped = DatabaseUtils.queryNumEntries(
- mDatabase,
- TABLE_DOCUMENTS,
- selection + " AND " +
- COLUMN_ROW_STATE + " = ? AND " +
- Document.COLUMN_DISPLAY_NAME + " = ?",
- strings(arg, ROW_STATE_UNMAPPED, displayName));
- if (numUnmapped != 0) {
- values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPING);
- }
- // Document ID is a primary integer key of the table. So the returned row IDs should
- // be same with the document ID.
- documentIds[i++] = mDatabase.insert(TABLE_DOCUMENTS, null, values);
- }
-
- mDatabase.setTransactionSuccessful();
- return documentIds;
- } finally {
- mDatabase.endTransaction();
+ synchronized boolean stopAddingRootDocuments(int deviceId) {
+ final String mappingModeKey = getRootDocumentsMappingStateKey(deviceId);
+ switch (mMappingMode.get(mappingModeKey)) {
+ case MAP_BY_MTP_IDENTIFIER:
+ mMappingMode.remove(mappingModeKey);
+ return mDatabase.stopAddingDocuments(
+ SELECTION_ROOT_DOCUMENTS,
+ Integer.toString(deviceId),
+ COLUMN_STORAGE_ID);
+ case MAP_BY_NAME:
+ mMappingMode.remove(mappingModeKey);
+ return mDatabase.stopAddingDocuments(
+ SELECTION_ROOT_DOCUMENTS,
+ Integer.toString(deviceId),
+ Document.COLUMN_DISPLAY_NAME);
+ default:
+ throw new Error("Unexpected mapping state.");
}
}
/**
- * Maps 'unmapped' document and 'mapping' document that don't have document but shares the same
- * name.
- * If the database does not find corresponding 'mapping' document, it just removes 'unmapped'
- * document from the database.
- * @param selection Query to select rows for resolving.
- * @param arg Argument for selection SQL.
+ * Stops adding documents under the parent.
+ * @param parentId Document ID of the parent.
*/
- private void resolveDocuments(String selection, String arg) {
- mDatabase.beginTransaction();
- try {
- // Get 1-to-1 mapping of unmapped document and mapping document.
- final String unmappedIdQuery = createStateFilter(
- ROW_STATE_UNMAPPED, Document.COLUMN_DOCUMENT_ID);
- final String mappingIdQuery = createStateFilter(
- ROW_STATE_MAPPING, Document.COLUMN_DOCUMENT_ID);
- // SQL should be like:
- // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
- // group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
- // WHERE device_id = ? AND parent_document_id IS NULL
- // GROUP BY display_name
- // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
- // count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
- final Cursor mergingCursor = mDatabase.query(
- TABLE_DOCUMENTS,
- new String[] {
- "group_concat(" + unmappedIdQuery + ")",
- "group_concat(" + mappingIdQuery + ")"
- },
- selection,
- strings(arg),
- Document.COLUMN_DISPLAY_NAME,
- "count(" + unmappedIdQuery + ") = 1 AND count(" + mappingIdQuery + ") = 1",
- null);
-
- final ContentValues values = new ContentValues();
- while (mergingCursor.moveToNext()) {
- final String unmappedId = mergingCursor.getString(0);
- final String mappingId = mergingCursor.getString(1);
-
- // Obtain the new values including the latest object handle from mapping row.
- getFirstRow(
- TABLE_DOCUMENTS,
- SELECTION_DOCUMENT_ID,
- new String[] { mappingId },
- values);
- values.remove(Document.COLUMN_DOCUMENT_ID);
- values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
- mDatabase.update(
- TABLE_DOCUMENTS,
- values,
- SELECTION_DOCUMENT_ID,
- new String[] { unmappedId });
-
- getFirstRow(
- TABLE_ROOT_EXTRA,
- SELECTION_ROOT_ID,
- new String[] { mappingId },
- values);
- if (values.size() > 0) {
- values.remove(Root.COLUMN_ROOT_ID);
- mDatabase.update(
- TABLE_ROOT_EXTRA,
- values,
- SELECTION_ROOT_ID,
- new String[] { unmappedId });
- }
-
- // Delete 'mapping' row.
- deleteDocumentsAndRoots(SELECTION_DOCUMENT_ID, new String[] { mappingId });
- }
- mergingCursor.close();
-
- // Delete all unmapped rows that cannot be mapped.
- deleteDocumentsAndRoots(
- COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_UNMAPPED, arg));
-
- // The database cannot find old document ID for the mapping rows.
- // Turn the all mapping rows into mapped state, which means the rows become to be
- // valid with new document ID.
- values.clear();
- values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
- mDatabase.update(
- TABLE_DOCUMENTS,
- values,
- COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_MAPPING, arg));
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
+ @VisibleForTesting
+ synchronized void stopAddingChildDocuments(String parentId) {
+ final String mappingModeKey = getChildDocumentsMappingStateKey(parentId);
+ switch (mMappingMode.get(mappingModeKey)) {
+ case MAP_BY_MTP_IDENTIFIER:
+ mDatabase.stopAddingDocuments(
+ SELECTION_CHILD_DOCUMENTS,
+ parentId,
+ COLUMN_OBJECT_HANDLE);
+ break;
+ case MAP_BY_NAME:
+ mDatabase.stopAddingDocuments(
+ SELECTION_CHILD_DOCUMENTS,
+ parentId,
+ Document.COLUMN_DISPLAY_NAME);
+ break;
+ default:
+ throw new Error("Unexpected mapping state.");
}
+ mMappingMode.remove(mappingModeKey);
}
/**
@@ -445,7 +342,7 @@
values.put(COLUMN_STORAGE_ID, root.mStorageId);
values.putNull(COLUMN_OBJECT_HANDLE);
values.putNull(COLUMN_PARENT_DOCUMENT_ID);
- values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
values.putNull(Document.COLUMN_SUMMARY);
@@ -482,7 +379,7 @@
values.put(COLUMN_STORAGE_ID, info.getStorageId());
values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
values.put(COLUMN_PARENT_DOCUMENT_ID, parentId);
- values.put(COLUMN_ROW_STATE, ROW_STATE_MAPPED);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
values.put(Document.COLUMN_MIME_TYPE, mimeType);
values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
values.putNull(Document.COLUMN_SUMMARY);
@@ -495,72 +392,18 @@
}
/**
- * Obtains values of the first row for the query.
- * @param values ContentValues that the values are stored to.
- * @param table Target table.
- * @param selection Query to select rows.
- * @param args Argument for query.
+ * @param deviceId Device ID.
+ * @return Key for {@link #mMappingMode}.
*/
- private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
- values.clear();
- final Cursor cursor = mDatabase.query(table, null, selection, args, null, null, null, "1");
- if (cursor.getCount() == 0) {
- return;
- }
- cursor.moveToNext();
- DatabaseUtils.cursorRowToContentValues(cursor, values);
- cursor.close();
+ private static String getRootDocumentsMappingStateKey(int deviceId) {
+ return "RootDocuments/" + deviceId;
}
/**
- * Deletes a document, and its root information if the document is a root document.
- * @param selection Query to select documents.
- * @param args Arguments for selection.
+ * @param parentDocumentId Document ID for the parent document.
+ * @return Key for {@link #mMappingMode}.
*/
- private void deleteDocumentsAndRoots(String selection, String[] args) {
- mDatabase.beginTransaction();
- try {
- mDatabase.delete(
- TABLE_ROOT_EXTRA,
- Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
- false,
- TABLE_DOCUMENTS,
- new String[] { Document.COLUMN_DOCUMENT_ID },
- selection,
- null,
- null,
- null,
- null) + ")",
- args);
- mDatabase.delete(TABLE_DOCUMENTS, selection, args);
- mDatabase.setTransactionSuccessful();
- } finally {
- mDatabase.endTransaction();
- }
- }
-
- /**
- * Converts values into string array.
- * @param args Values converted into string array.
- * @return String array.
- */
- private static String[] strings(Object... args) {
- final String[] results = new String[args.length];
- for (int i = 0; i < args.length; i++) {
- results[i] = Objects.toString(args[i]);
- }
- return results;
- }
-
- /**
- * Gets SQL expression that represents the given value or NULL depends on the row state.
- * @param state Expected row state.
- * @param a SQL value.
- * @return Expression that represents a if the row state is expected one, and represents NULL
- * otherwise.
- */
- private static String createStateFilter(int state, String a) {
- return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
- " THEN " + a + " ELSE NULL END";
+ private static String getChildDocumentsMappingStateKey(String parentDocumentId) {
+ return "ChildDocuments/" + parentDocumentId;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
new file mode 100644
index 0000000..97c1d29
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -0,0 +1,148 @@
+/*
+ * 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.mtp;
+
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+
+/**
+ * Class containing MtpDatabase constants.
+ */
+class MtpDatabaseConstants {
+ static final int DATABASE_VERSION = 1;
+ static final String DATABASE_NAME = null;
+
+ static final int FLAG_DATABASE_IN_MEMORY = 1;
+ static final int FLAG_DATABASE_IN_FILE = 0;
+
+ /**
+ * Table representing documents including root documents.
+ */
+ static final String TABLE_DOCUMENTS = "Documents";
+
+ /**
+ * Table containing additional information only available for root documents.
+ * The table uses same primary keys with corresponding documents.
+ */
+ static final String TABLE_ROOT_EXTRA = "RootExtra";
+
+ /**
+ * View to join Documents and RootExtra tables to provide roots information.
+ */
+ static final String VIEW_ROOTS = "Roots";
+
+ static final String COLUMN_DEVICE_ID = "device_id";
+ static final String COLUMN_STORAGE_ID = "storage_id";
+ static final String COLUMN_OBJECT_HANDLE = "object_handle";
+ static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
+ static final String COLUMN_ROW_STATE = "row_state";
+
+ /**
+ * The state represents that the row has a valid object handle.
+ */
+ static final int ROW_STATE_VALID = 0;
+
+ /**
+ * The state represents that the rows added at the previous cycle and need to be updated with
+ * fresh values.
+ * The row may not have valid object handle. External application can still fetch the documents.
+ * If the external application tries to fetch object handle, the provider resolves pending
+ * documents with invalidated documents ahead.
+ */
+ static final int ROW_STATE_INVALIDATED = 1;
+
+ /**
+ * The state represents the raw has a valid object handle but it may be going to be mapped with
+ * another rows invalidated. After fetching all documents under the parent, the database tries
+ * to map the pending documents and the invalidated documents in order to keep old document ID
+ * alive.
+ */
+ static final int ROW_STATE_PENDING = 2;
+
+ /**
+ * Mapping mode that uses MTP identifier to find corresponding rows.
+ */
+ static final int MAP_BY_MTP_IDENTIFIER = 0;
+
+ /**
+ * Mapping mode that uses name to find corresponding rows.
+ */
+ static final int MAP_BY_NAME = 1;
+
+ static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
+ static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
+ static final String SELECTION_ROOT_DOCUMENTS =
+ COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+ static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?";
+
+ static final String QUERY_CREATE_DOCUMENTS =
+ "CREATE TABLE " + TABLE_DOCUMENTS + " (" +
+ Document.COLUMN_DOCUMENT_ID +
+ " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
+ COLUMN_STORAGE_ID + " INTEGER," +
+ COLUMN_OBJECT_HANDLE + " INTEGER," +
+ COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
+ COLUMN_ROW_STATE + " INTEGER NOT NULL," +
+ Document.COLUMN_MIME_TYPE + " TEXT," +
+ Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
+ Document.COLUMN_SUMMARY + " TEXT," +
+ Document.COLUMN_LAST_MODIFIED + " INTEGER," +
+ Document.COLUMN_ICON + " INTEGER," +
+ Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
+ Document.COLUMN_SIZE + " INTEGER NOT NULL);";
+
+ static final String QUERY_CREATE_ROOT_EXTRA =
+ "CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
+ Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
+ Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
+ Root.COLUMN_AVAILABLE_BYTES + " INTEGER NOT NULL," +
+ Root.COLUMN_CAPACITY_BYTES + " INTEGER NOT NULL," +
+ Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
+
+ /**
+ * Creates a view to join Documents table and RootExtra table on their primary keys to
+ * provide DocumentContract.Root equivalent information.
+ */
+ static final String QUERY_CREATE_VIEW_ROOTS =
+ "CREATE VIEW " + VIEW_ROOTS + " AS SELECT " +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
+ Root.COLUMN_ROOT_ID + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " +
+ Root.COLUMN_ICON + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " +
+ Root.COLUMN_TITLE + "," +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " +
+ Root.COLUMN_SUMMARY + "," +
+ // Temporary replace COLUMN_DOCUMENT_ID with old format.
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID + " AS " +
+ Root.COLUMN_DOCUMENT_ID + "_," +
+ TABLE_DOCUMENTS + "." + COLUMN_DEVICE_ID + "|| '_' ||" +
+ TABLE_DOCUMENTS + "." + COLUMN_STORAGE_ID + "||'_0' AS " +
+ Root.COLUMN_DOCUMENT_ID + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES + "," +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES + "," +
+ TABLE_DOCUMENTS + "." + COLUMN_ROW_STATE +
+ " FROM " + TABLE_DOCUMENTS + " INNER JOIN " + TABLE_ROOT_EXTRA +
+ " ON " +
+ COLUMN_PARENT_DOCUMENT_ID + " IS NULL AND " +
+ TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
+ "=" +
+ TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID;
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java
new file mode 100644
index 0000000..9c5d6b6
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseInternal.java
@@ -0,0 +1,515 @@
+/*
+ * 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.mtp;
+
+import static com.android.mtp.MtpDatabaseConstants.*;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+
+import java.util.Objects;
+
+/**
+ * Class that provides operations processing SQLite database directly.
+ */
+class MtpDatabaseInternal {
+ private static class OpenHelper extends SQLiteOpenHelper {
+ public OpenHelper(Context context, int flags) {
+ super(context,
+ flags == FLAG_DATABASE_IN_MEMORY ? null : DATABASE_NAME,
+ null,
+ DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(QUERY_CREATE_DOCUMENTS);
+ db.execSQL(QUERY_CREATE_ROOT_EXTRA);
+ db.execSQL(QUERY_CREATE_VIEW_ROOTS);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private final SQLiteDatabase mDatabase;
+
+ MtpDatabaseInternal(Context context, int flags) {
+ final OpenHelper helper = new OpenHelper(context, flags);
+ mDatabase = helper.getWritableDatabase();
+ }
+
+ void close() {
+ mDatabase.close();
+ }
+
+ /**
+ * Queries roots information.
+ * @param columnNames Column names defined in {@link android.provider.DocumentsContract.Root}.
+ * @return Database cursor.
+ */
+ Cursor queryRoots(String[] columnNames) {
+ return mDatabase.query(
+ VIEW_ROOTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?)",
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
+ null,
+ null,
+ null);
+ }
+
+ /**
+ * Queries root documents information.
+ * @param columnNames Column names defined in
+ * {@link android.provider.DocumentsContract.Document}.
+ * @return Database cursor.
+ */
+ Cursor queryRootDocuments(String[] columnNames) {
+ return mDatabase.query(
+ TABLE_DOCUMENTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?)",
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
+ null,
+ null,
+ null);
+ }
+
+ /**
+ * Queries documents information.
+ * @param columnNames Column names defined in
+ * {@link android.provider.DocumentsContract.Document}.
+ * @return Database cursor.
+ */
+ Cursor queryChildDocuments(String[] columnNames, String parentDocumentId) {
+ return mDatabase.query(
+ TABLE_DOCUMENTS,
+ columnNames,
+ COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_PARENT_DOCUMENT_ID + " = ?",
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, parentDocumentId),
+ null,
+ null,
+ null);
+ }
+
+ /**
+ * Remove all rows belong to a device.
+ * @param deviceId Device ID.
+ */
+ void removeDeviceRows(int deviceId) {
+ deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
+ }
+
+ /**
+ * Gets identifier from document ID.
+ * @param documentId Document ID.
+ * @return Identifier.
+ */
+ Identifier createIdentifier(String documentId) {
+ // Currently documentId is old format.
+ final Identifier oldIdentifier = Identifier.createFromDocumentId(documentId);
+ final String selection;
+ final String[] args;
+ if (oldIdentifier.mObjectHandle == CursorHelper.DUMMY_HANDLE_FOR_ROOT) {
+ selection = COLUMN_DEVICE_ID + "= ? AND " +
+ COLUMN_ROW_STATE + " IN (?, ?) AND " +
+ COLUMN_STORAGE_ID + "= ? AND " +
+ COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+ args = strings(
+ oldIdentifier.mDeviceId,
+ ROW_STATE_VALID,
+ ROW_STATE_INVALIDATED,
+ oldIdentifier.mStorageId);
+ } else {
+ selection = COLUMN_DEVICE_ID + "= ? AND " +
+ COLUMN_ROW_STATE + " IN (?, ?) AND " +
+ COLUMN_STORAGE_ID + "= ? AND " +
+ COLUMN_OBJECT_HANDLE + " = ?";
+ args = strings(
+ oldIdentifier.mDeviceId,
+ ROW_STATE_VALID,
+ ROW_STATE_INVALIDATED,
+ oldIdentifier.mStorageId,
+ oldIdentifier.mObjectHandle);
+ }
+
+ final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ selection,
+ args,
+ null,
+ null,
+ null,
+ "1");
+ try {
+ if (cursor.getCount() == 0) {
+ return oldIdentifier;
+ } else {
+ cursor.moveToNext();
+ return new Identifier(
+ oldIdentifier.mDeviceId,
+ oldIdentifier.mStorageId,
+ oldIdentifier.mObjectHandle,
+ cursor.getString(0));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Starts adding new documents.
+ * The methods decides mapping mode depends on if all documents under the given parent have MTP
+ * identifier or not. If all the documents have MTP identifier, it uses the identifier to find
+ * a corresponding existing row. Otherwise it does heuristic.
+ *
+ * @param selection Query matches valid documents.
+ * @param arg Argument for selection.
+ * @return Mapping mode.
+ */
+ int startAddingDocuments(String selection, String arg) {
+ mDatabase.beginTransaction();
+ try {
+ // Delete all pending rows.
+ deleteDocumentsAndRoots(
+ selection + " AND " + COLUMN_ROW_STATE + "=?", strings(arg, ROW_STATE_PENDING));
+
+ // Set all documents as invalidated.
+ final ContentValues values = new ContentValues();
+ values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
+ mDatabase.update(TABLE_DOCUMENTS, values, selection, new String[] { arg });
+
+ // If we have rows that does not have MTP identifier, do heuristic mapping by name.
+ final boolean useNameForResolving = DatabaseUtils.queryNumEntries(
+ mDatabase,
+ TABLE_DOCUMENTS,
+ selection + " AND " + COLUMN_STORAGE_ID + " IS NULL",
+ new String[] { arg }) > 0;
+ mDatabase.setTransactionSuccessful();
+ return useNameForResolving ? MAP_BY_NAME : MAP_BY_MTP_IDENTIFIER;
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Puts the documents into the database.
+ * If the mapping mode is not heuristic, it just adds the rows to the database or updates the
+ * existing rows with the new values. If the mapping mode is heuristic, it adds some new rows as
+ * 'pending' state when that rows may be corresponding to existing 'invalidated' rows. Then
+ * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid'
+ * rows. If the methods adds rows to database, it updates valueList with correct document ID.
+ *
+ * @param valuesList Values for documents to be stored in the database.
+ * @param selection SQL where closure to select rows that shares the same parent.
+ * @param arg Argument for selection SQL.
+ * @param heuristic Whether the mapping mode is heuristic.
+ * @return Whether the method adds new rows.
+ */
+ boolean putDocuments(
+ ContentValues[] valuesList,
+ String selection,
+ String arg,
+ boolean heuristic,
+ String mappingKey) {
+ boolean added = false;
+ mDatabase.beginTransaction();
+ try {
+ for (final ContentValues values : valuesList) {
+ final Cursor candidateCursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ selection + " AND " +
+ COLUMN_ROW_STATE + "=? AND " +
+ mappingKey + "=?",
+ strings(arg, ROW_STATE_INVALIDATED, values.getAsString(mappingKey)),
+ null,
+ null,
+ null,
+ "1");
+ try {
+ final long rowId;
+ if (candidateCursor.getCount() == 0) {
+ rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values);
+ if (rowId == -1) {
+ throw new SQLiteException("Failed to put a document into database.");
+ }
+ added = true;
+ } else if (!heuristic) {
+ candidateCursor.moveToNext();
+ final String documentId = candidateCursor.getString(0);
+ rowId = mDatabase.update(
+ TABLE_DOCUMENTS,
+ values,
+ SELECTION_DOCUMENT_ID,
+ strings(documentId));
+ } else {
+ values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
+ rowId = mDatabase.insert(TABLE_DOCUMENTS, null, values);
+ }
+ // Document ID is a primary integer key of the table. So the returned row
+ // IDs should be same with the document ID.
+ values.put(Document.COLUMN_DOCUMENT_ID, rowId);
+ } finally {
+ candidateCursor.close();
+ }
+ }
+
+ mDatabase.setTransactionSuccessful();
+ return added;
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Puts extra information for root documents.
+ * @param values Values containing extra information.
+ */
+ void putRootExtra(ContentValues values) {
+ mDatabase.replace(TABLE_ROOT_EXTRA, null, values);
+ }
+
+ /**
+ * Maps 'pending' document and 'invalidated' document that shares the same column of groupKey.
+ * If the database does not find corresponding 'invalidated' document, it just removes
+ * 'invalidated' document from the database.
+ * @param selection Query to select rows for resolving.
+ * @param arg Argument for selection SQL.
+ * @param groupKey Column name used to find corresponding rows.
+ * @return Whether the methods adds or removed visible rows.
+ */
+ boolean stopAddingDocuments(String selection, String arg, String groupKey) {
+ mDatabase.beginTransaction();
+ try {
+ // Get 1-to-1 mapping of invalidated document and pending document.
+ final String invalidatedIdQuery = createStateFilter(
+ ROW_STATE_INVALIDATED, Document.COLUMN_DOCUMENT_ID);
+ final String pendingIdQuery = createStateFilter(
+ ROW_STATE_PENDING, Document.COLUMN_DOCUMENT_ID);
+ // SQL should be like:
+ // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
+ // group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
+ // WHERE device_id = ? AND parent_document_id IS NULL
+ // GROUP BY display_name
+ // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
+ // count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
+ final Cursor mergingCursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ new String[] {
+ "group_concat(" + invalidatedIdQuery + ")",
+ "group_concat(" + pendingIdQuery + ")"
+ },
+ selection,
+ strings(arg),
+ groupKey,
+ "count(" + invalidatedIdQuery + ") = 1 AND count(" + pendingIdQuery + ") = 1",
+ null);
+
+ final ContentValues values = new ContentValues();
+ while (mergingCursor.moveToNext()) {
+ final String invalidatedId = mergingCursor.getString(0);
+ final String pendingId = mergingCursor.getString(1);
+
+ // Obtain the new values including the latest object handle from mapping row.
+ getFirstRow(
+ TABLE_DOCUMENTS,
+ SELECTION_DOCUMENT_ID,
+ new String[] { pendingId },
+ values);
+ values.remove(Document.COLUMN_DOCUMENT_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+ mDatabase.update(
+ TABLE_DOCUMENTS,
+ values,
+ SELECTION_DOCUMENT_ID,
+ new String[] { invalidatedId });
+
+ getFirstRow(
+ TABLE_ROOT_EXTRA,
+ SELECTION_ROOT_ID,
+ new String[] { pendingId },
+ values);
+ if (values.size() > 0) {
+ values.remove(Root.COLUMN_ROOT_ID);
+ mDatabase.update(
+ TABLE_ROOT_EXTRA,
+ values,
+ SELECTION_ROOT_ID,
+ new String[] { invalidatedId });
+ }
+
+ // Delete 'pending' row.
+ deleteDocumentsAndRoots(SELECTION_DOCUMENT_ID, new String[] { pendingId });
+ }
+ mergingCursor.close();
+
+ boolean changed = false;
+
+ // Delete all invalidated rows that cannot be mapped.
+ if (deleteDocumentsAndRoots(
+ COLUMN_ROW_STATE + " = ? AND " + selection,
+ strings(ROW_STATE_INVALIDATED, arg))) {
+ changed = true;
+ }
+
+ // The database cannot find old document ID for the pending rows.
+ // Turn the all pending rows into valid state, which means the rows become to be
+ // valid with new document ID.
+ values.clear();
+ values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+ if (mDatabase.update(
+ TABLE_DOCUMENTS,
+ values,
+ COLUMN_ROW_STATE + " = ? AND " + selection,
+ strings(ROW_STATE_PENDING, arg)) != 0) {
+ changed = true;
+ }
+ mDatabase.setTransactionSuccessful();
+ return changed;
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Clears MTP related identifier.
+ * It clears MTP's object handle and storage ID that are not stable over MTP sessions and mark
+ * the all documents as 'invalidated'. It also remove 'pending' rows as adding is cancelled
+ * now.
+ */
+ void clearMapping() {
+ mDatabase.beginTransaction();
+ try {
+ deleteDocumentsAndRoots(COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
+ final ContentValues values = new ContentValues();
+ values.putNull(COLUMN_OBJECT_HANDLE);
+ values.putNull(COLUMN_STORAGE_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
+ mDatabase.update(TABLE_DOCUMENTS, values, null, null);
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#beginTransaction()}
+ */
+ void beginTransaction() {
+ mDatabase.beginTransaction();
+ }
+
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#setTransactionSuccessful()}
+ */
+ void setTransactionSuccessful() {
+ mDatabase.setTransactionSuccessful();
+ }
+
+ /**
+ * {@link android.database.sqlite.SQLiteDatabase#endTransaction()}
+ */
+ void endTransaction() {
+ mDatabase.endTransaction();
+ }
+
+ /**
+ * Deletes a document, and its root information if the document is a root document.
+ * @param selection Query to select documents.
+ * @param args Arguments for selection.
+ * @return Whether the method deletes rows.
+ */
+ private boolean deleteDocumentsAndRoots(String selection, String[] args) {
+ mDatabase.beginTransaction();
+ try {
+ int deleted = 0;
+ deleted += mDatabase.delete(
+ TABLE_ROOT_EXTRA,
+ Root.COLUMN_ROOT_ID + " IN (" + SQLiteQueryBuilder.buildQueryString(
+ false,
+ TABLE_DOCUMENTS,
+ new String[] { Document.COLUMN_DOCUMENT_ID },
+ selection,
+ null,
+ null,
+ null,
+ null) + ")",
+ args);
+ deleted += mDatabase.delete(TABLE_DOCUMENTS, selection, args);
+ mDatabase.setTransactionSuccessful();
+ return deleted != 0;
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ /**
+ * Obtains values of the first row for the query.
+ * @param values ContentValues that the values are stored to.
+ * @param table Target table.
+ * @param selection Query to select rows.
+ * @param args Argument for query.
+ */
+ private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
+ values.clear();
+ final Cursor cursor = mDatabase.query(table, null, selection, args, null, null, null, "1");
+ if (cursor.getCount() == 0) {
+ return;
+ }
+ cursor.moveToNext();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ cursor.close();
+ }
+
+ /**
+ * Gets SQL expression that represents the given value or NULL depends on the row state.
+ * @param state Expected row state.
+ * @param a SQL value.
+ * @return Expression that represents a if the row state is expected one, and represents NULL
+ * otherwise.
+ */
+ private static String createStateFilter(int state, String a) {
+ return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
+ " THEN " + a + " ELSE NULL END";
+ }
+
+ /**
+ * Converts values into string array.
+ * @param args Values converted into string array.
+ * @return String array.
+ */
+ private static String[] strings(Object... args) {
+ final String[] results = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ results[i] = Objects.toString(args[i]);
+ }
+ return results;
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 7883e61..7c0676f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -31,6 +31,7 @@
import android.provider.DocumentsProvider;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
@@ -59,9 +60,11 @@
private MtpManager mMtpManager;
private ContentResolver mResolver;
+ @GuardedBy("mDeviceToolkits")
private Map<Integer, DeviceToolkit> mDeviceToolkits;
private RootScanner mRootScanner;
private Resources mResources;
+ private MtpDatabase mDatabase;
/**
* Provides singleton instance to MtpDocumentsService.
@@ -77,17 +80,23 @@
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
- mRootScanner = new RootScanner(mResolver, mMtpManager);
+ mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
+ mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
return true;
}
@VisibleForTesting
- void onCreateForTesting(Resources resources, MtpManager mtpManager, ContentResolver resolver) {
+ void onCreateForTesting(
+ Resources resources,
+ MtpManager mtpManager,
+ ContentResolver resolver,
+ MtpDatabase database) {
mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
- mRootScanner = new RootScanner(mResolver, mMtpManager);
+ mDatabase = database;
+ mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
}
@Override
@@ -95,17 +104,7 @@
if (projection == null) {
projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
}
- final MatrixCursor cursor = new MatrixCursor(projection);
- final MtpRoot[] roots = mRootScanner.getRoots();
- for (final MtpRoot root : roots) {
- final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId);
- final MatrixCursor.RowBuilder builder = cursor.newRow();
- builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
- builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
- builder.add(Root.COLUMN_TITLE, root.getRootName(mResources));
- builder.add(Root.COLUMN_DOCUMENT_ID, rootIdentifier.toDocumentId());
- builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
- }
+ final Cursor cursor = mDatabase.queryRoots(projection);
cursor.setNotificationUri(
mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
return cursor;
@@ -158,7 +157,7 @@
if (projection == null) {
projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
}
- final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
+ final Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId);
try {
return getDocumentLoader(parentIdentifier).queryChildDocuments(
projection, parentIdentifier);
@@ -225,9 +224,11 @@
@Override
public void onTrimMemory(int level) {
- for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
- toolkit.mDocumentLoader.clearCompletedTasks();
- }
+ synchronized (mDeviceToolkits) {
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ toolkit.mDocumentLoader.clearCompletedTasks();
+ }
+ }
}
@Override
@@ -257,23 +258,32 @@
}
void openDevice(int deviceId) throws IOException {
- mMtpManager.openDevice(deviceId);
- mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver));
- mRootScanner.scanNow();
+ synchronized (mDeviceToolkits) {
+ mMtpManager.openDevice(deviceId);
+ mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase));
+ }
+ mRootScanner.resume();
}
- void closeDevice(int deviceId) throws IOException {
+ void closeDevice(int deviceId) throws IOException, InterruptedException {
// TODO: Flush the device before closing (if not closed externally).
- getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
- mDeviceToolkits.remove(deviceId);
- mMtpManager.closeDevice(deviceId);
- mRootScanner.scanNow();
+ synchronized (mDeviceToolkits) {
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
+ mDeviceToolkits.remove(deviceId);
+ mDatabase.removeDeviceRows(deviceId);
+ mMtpManager.closeDevice(deviceId);
+ }
+ mRootScanner.notifyChange();
+ if (!hasOpenedDevices()) {
+ mRootScanner.pause();
+ }
}
- void closeAllDevices() {
+ synchronized void closeAllDevices() throws InterruptedException {
boolean closed = false;
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
+ mDatabase.removeDeviceRows(deviceId);
mMtpManager.closeDevice(deviceId);
getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
closed = true;
@@ -282,7 +292,8 @@
}
}
if (closed) {
- mRootScanner.scanNow();
+ mRootScanner.notifyChange();
+ mRootScanner.pause();
}
}
@@ -290,6 +301,22 @@
return mMtpManager.getOpenedDeviceIds().length != 0;
}
+ /**
+ * Finalize the content provider for unit tests.
+ */
+ @Override
+ public void shutdown() {
+ try {
+ closeAllDevices();
+ } catch (InterruptedException e) {
+ // It should fail unit tests by throwing runtime exception.
+ throw new RuntimeException(e.getMessage());
+ } finally {
+ mDatabase.close();
+ super.shutdown();
+ }
+ }
+
private void notifyChildDocumentsChange(String parentDocumentId) {
mResolver.notifyChange(
DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId),
@@ -298,11 +325,13 @@
}
private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
- final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
- if (toolkit == null) {
- throw new FileNotFoundException();
+ synchronized (mDeviceToolkits) {
+ final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
+ if (toolkit == null) {
+ throw new FileNotFoundException();
+ }
+ return toolkit;
}
- return toolkit;
}
private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException {
@@ -317,9 +346,9 @@
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
- public DeviceToolkit(MtpManager manager, ContentResolver resolver) {
+ public DeviceToolkit(MtpManager manager, ContentResolver resolver, MtpDatabase database) {
mPipeManager = new PipeManager();
- mDocumentLoader = new DocumentLoader(manager, resolver);
+ mDocumentLoader = new DocumentLoader(manager, resolver, database);
}
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 328f618..723dc14 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -67,10 +67,10 @@
provider.openDevice(device.getDeviceId());
return START_STICKY;
} catch (IOException error) {
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
+ Log.e(MtpDocumentsProvider.TAG, error.getMessage());
}
} else {
- Log.d(MtpDocumentsProvider.TAG, "Received unknown intent action.");
+ Log.w(MtpDocumentsProvider.TAG, "Received unknown intent action.");
}
stopSelfIfNeeded();
return Service.START_NOT_STICKY;
@@ -79,9 +79,13 @@
@Override
public void onDestroy() {
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
- provider.closeAllDevices();
unregisterReceiver(mReceiver);
mReceiver = null;
+ try {
+ provider.closeAllDevices();
+ } catch (InterruptedException e) {
+ Log.e(MtpDocumentsProvider.TAG, e.getMessage());
+ }
super.onDestroy();
}
@@ -101,8 +105,8 @@
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
try {
provider.closeDevice(device.getDeviceId());
- } catch (IOException error) {
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
+ } catch (IOException | InterruptedException error) {
+ Log.e(MtpDocumentsProvider.TAG, error.getMessage());
}
stopSelfIfNeeded();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index ffab176..b0962dd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -1,14 +1,18 @@
package com.android.mtp;
import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Process;
import android.provider.DocumentsContract;
import android.util.Log;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
final class RootScanner {
/**
@@ -27,79 +31,118 @@
*/
private final static long SHORT_POLLING_TIMES = 10;
- final ContentResolver mResolver;
- final MtpManager mManager;
- MtpRoot[] mLastRoots = new MtpRoot[0];
- int mPollingCount;
- boolean mHasBackgroundTask = false;
+ /**
+ * Milliseconds we wait for background thread when pausing.
+ */
+ private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
- RootScanner(ContentResolver resolver, MtpManager manager) {
+ final ContentResolver mResolver;
+ final Resources mResources;
+ final MtpManager mManager;
+ final MtpDatabase mDatabase;
+
+ ExecutorService mExecutor;
+ FutureTask<Void> mCurrentTask;
+
+ RootScanner(
+ ContentResolver resolver,
+ Resources resources,
+ MtpManager manager,
+ MtpDatabase database) {
mResolver = resolver;
+ mResources = resources;
mManager = manager;
+ mDatabase = database;
}
- synchronized MtpRoot[] getRoots() {
- return mLastRoots;
+ /**
+ * Notifies a change of the roots list via ContentResolver.
+ */
+ void notifyChange() {
+ final Uri uri =
+ DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
+ mResolver.notifyChange(uri, null, false);
}
/**
* Starts to check new changes right away.
* If the background thread has already gone, it restarts another background thread.
*/
- synchronized void scanNow() {
- mPollingCount = 0;
- if (!mHasBackgroundTask) {
- mHasBackgroundTask = true;
- new BackgroundLoaderThread().start();
- } else {
- notify();
+ synchronized void resume() {
+ if (mExecutor == null) {
+ // Only single thread updates the database.
+ mExecutor = Executors.newSingleThreadExecutor();
}
+ if (mCurrentTask != null) {
+ // Cancel previous task.
+ mCurrentTask.cancel(true);
+ }
+ mCurrentTask = new FutureTask<Void>(new UpdateRootsRunnable(), null);
+ mExecutor.submit(mCurrentTask);
}
- private MtpRoot[] createRoots(int[] deviceIds) {
- final ArrayList<MtpRoot> roots = new ArrayList<>();
- for (final int deviceId : deviceIds) {
- try {
- roots.addAll(Arrays.asList(mManager.getRoots(deviceId)));
- } catch (IOException error) {
- // Skip device that return error.
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
- }
+ /**
+ * Stops background thread and wait for its termination.
+ * @throws InterruptedException
+ */
+ synchronized void pause() throws InterruptedException {
+ if (mExecutor == null) {
+ return;
}
- return roots.toArray(new MtpRoot[roots.size()]);
+ mExecutor.shutdownNow();
+ if (!mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to terminate RootScanner's background thread.");
+ }
+ mExecutor = null;
}
- private final class BackgroundLoaderThread extends Thread {
+ /**
+ * Runnable to scan roots and update the database information.
+ */
+ private final class UpdateRootsRunnable implements Runnable {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- synchronized (RootScanner.this) {
- while (true) {
- final int[] deviceIds = mManager.getOpenedDeviceIds();
- final MtpRoot[] newRoots = createRoots(deviceIds);
- if (!Arrays.equals(mLastRoots, newRoots)) {
- final Uri uri =
- DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
- mResolver.notifyChange(uri, null, false);
- mLastRoots = newRoots;
- }
- if (deviceIds.length == 0) {
- break;
- }
- mPollingCount++;
+ int pollingCount = 0;
+ while (!Thread.interrupted()) {
+ final int[] deviceIds = mManager.getOpenedDeviceIds();
+ if (deviceIds.length == 0) {
+ return;
+ }
+ boolean changed = false;
+ for (int deviceId : deviceIds) {
try {
- // Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
- // more likely to add new root just after the device is added.
- // TODO: Use short interval only for a device that is just added.
- RootScanner.this.wait(
- mPollingCount > SHORT_POLLING_TIMES ?
- LONG_POLLING_INTERVAL : SHORT_POLLING_INTERVAL);
- } catch (InterruptedException exception) {
- break;
+ final MtpRoot[] roots = mManager.getRoots(deviceId);
+ mDatabase.startAddingRootDocuments(deviceId);
+ try {
+ if (mDatabase.putRootDocuments(deviceId, mResources, roots)) {
+ changed = true;
+ }
+ } finally {
+ if (mDatabase.stopAddingRootDocuments(deviceId)) {
+ changed = true;
+ }
+ }
+ } catch (IOException | SQLiteException exception) {
+ // The error may happen on the device. We would like to continue getting
+ // roots for other devices.
+ Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
}
}
-
- mHasBackgroundTask = false;
+ if (changed) {
+ notifyChange();
+ }
+ pollingCount++;
+ try {
+ // Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
+ // more likely to add new root just after the device is added.
+ // TODO: Use short interval only for a device that is just added.
+ Thread.sleep(pollingCount > SHORT_POLLING_TIMES ?
+ LONG_POLLING_INTERVAL : SHORT_POLLING_INTERVAL);
+ } catch (InterruptedException exp) {
+ // The while condition handles the interrupted flag.
+ continue;
+ }
}
}
}
diff --git a/packages/MtpDocumentsProvider/tests/AndroidManifest.xml b/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
index 28ad3f4..e1307e9 100644
--- a/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/tests/AndroidManifest.xml
@@ -18,7 +18,4 @@
<instrumentation android:name="com.android.mtp.TestResultInstrumentation"
android:targetPackage="com.android.mtp"
android:label="Tests for MtpDocumentsProvider with the UI for output." />
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.mtp"
- android:label="Tests for MtpDocumentsProvider." />
</manifest>
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index a012d7f..a80eb51 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -22,26 +22,37 @@
import android.net.Uri;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
import java.io.IOException;
-import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
-@SmallTest
+@MediumTest
public class DocumentLoaderTest extends AndroidTestCase {
+ private MtpDatabase mDatabase;
private BlockableTestMtpManager mManager;
private TestContentResolver mResolver;
private DocumentLoader mLoader;
- final private Identifier mParentIdentifier = new Identifier(0, 0, 0);
+ final private Identifier mParentIdentifier = new Identifier(0, 0, 0, "1");
@Override
public void setUp() {
+ mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, new TestResources(), new MtpRoot[] {
+ new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
+ });
+ mDatabase.stopAddingRootDocuments(0);
mManager = new BlockableTestMtpManager(getContext());
mResolver = new TestContentResolver();
- mLoader = new DocumentLoader(mManager, mResolver);
+ mLoader = new DocumentLoader(mManager, mResolver, mDatabase);
+ }
+
+ @Override
+ public void tearDown() {
+ mDatabase.close();
}
public void testBasic() throws Exception {
@@ -88,6 +99,7 @@
childDocuments[i] = objectHandle;
manager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(objectHandle)
+ .setName(Integer.toString(i))
.build());
}
manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments);
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 3878ba6..25dd1c8 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -28,9 +28,9 @@
public class MtpDatabaseTest extends AndroidTestCase {
private final String[] COLUMN_NAMES = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_DEVICE_ID,
- MtpDatabase.COLUMN_STORAGE_ID,
- MtpDatabase.COLUMN_OBJECT_HANDLE,
+ MtpDatabaseConstants.COLUMN_DEVICE_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
+ MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_SUMMARY,
@@ -41,22 +41,29 @@
};
private final TestResources resources = new TestResources();
+ MtpDatabase mDatabase;
+
+ @Override
+ public void setUp() {
+ mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ }
@Override
public void tearDown() {
- MtpDatabase.deleteDatabase(getContext());
+ mDatabase.close();
+ mDatabase = null;
}
public void testPutRootDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""),
new MtpRoot(0, 2, "Device", "Storage", 2000, 4000, ""),
new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 3000, 6000,"")
});
{
- final Cursor cursor = database.queryRootDocuments(COLUMN_NAMES);
+ final Cursor cursor = mDatabase.queryRootDocuments(COLUMN_NAMES);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -84,13 +91,13 @@
}
{
- final Cursor cursor = database.queryRoots(new String [] {
+ final Cursor cursor = mDatabase.queryRoots(new String [] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_FLAGS,
Root.COLUMN_ICON,
Root.COLUMN_TITLE,
Root.COLUMN_SUMMARY,
- Root.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_DOCUMENT_ID + "_",
Root.COLUMN_AVAILABLE_BYTES,
Root.COLUMN_CAPACITY_BYTES
});
@@ -140,15 +147,14 @@
}
public void testPutChildDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
-
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
});
- final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(COLUMN_NAMES, "parentId");
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -206,23 +212,24 @@
}
public void testRestoreIdForRootDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_STORAGE_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
final String[] rootColumns = new String[] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.putRootDocuments(0, resources, new MtpRoot[] {
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 1000, 0, ""),
new MtpRoot(0, 101, "Device", "Storage B", 1001, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -236,7 +243,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -247,10 +254,10 @@
cursor.close();
}
- database.clearMapping();
+ mDatabase.clearMapping();
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -264,7 +271,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -275,13 +282,14 @@
cursor.close();
}
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage A", 2000, 0, ""),
new MtpRoot(0, 202, "Device", "Storage C", 2002, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -299,7 +307,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -313,10 +321,10 @@
cursor.close();
}
- database.resolveRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -330,7 +338,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -343,21 +351,21 @@
}
public void testRestoreIdForChildDocuments() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_OBJECT_HANDLE,
+ MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
});
- database.clearMapping();
+ mDatabase.clearMapping();
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(3, cursor.getCount());
cursor.moveToNext();
@@ -378,13 +386,14 @@
cursor.close();
}
- database.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
+ mDatabase.startAddingChildDocuments("parentId");
+ mDatabase.putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
});
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(4, cursor.getCount());
cursor.moveToPosition(3);
@@ -395,10 +404,10 @@
cursor.close();
}
- database.resolveChildDocuments("parentId");
+ mDatabase.stopAddingChildDocuments("parentId");
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId");
assertEquals(2, cursor.getCount());
cursor.moveToNext();
@@ -415,25 +424,26 @@
}
public void testRestoreIdForDifferentDevices() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_STORAGE_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
final String[] rootColumns = new String[] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.startAddingRootDocuments(1);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, "")
});
- database.putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(1, resources, new MtpRoot[] {
new MtpRoot(1, 100, "Device", "Storage", 0, 0, "")
});
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -447,7 +457,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -458,19 +468,21 @@
cursor.close();
}
- database.clearMapping();
+ mDatabase.clearMapping();
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.startAddingRootDocuments(1);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, "")
});
- database.putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.putRootDocuments(1, resources, new MtpRoot[] {
new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
});
- database.resolveRootDocuments(0);
- database.resolveRootDocuments(1);
+ mDatabase.stopAddingRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(1);
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -484,7 +496,7 @@
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -497,28 +509,33 @@
}
public void testRestoreIdForDifferentParents() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_OBJECT_HANDLE
+ MtpDatabaseConstants.COLUMN_OBJECT_HANDLE
};
- database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+
+ mDatabase.startAddingChildDocuments("parentId1");
+ mDatabase.startAddingChildDocuments("parentId2");
+ mDatabase.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ mDatabase.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.clearMapping();
- database.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
+ mDatabase.clearMapping();
+
+ mDatabase.startAddingChildDocuments("parentId1");
+ mDatabase.startAddingChildDocuments("parentId2");
+ mDatabase.putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
+ mDatabase.putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- database.resolveChildDocuments("parentId1");
+ mDatabase.stopAddingChildDocuments("parentId1");
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId1");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId1");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -526,7 +543,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryChildDocuments(columns, "parentId2");
+ final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId2");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 2, cursor.getInt(0));
@@ -536,30 +553,36 @@
}
public void testClearMtpIdentifierBeforeResolveRootDocuments() {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_STORAGE_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
final String[] rootColumns = new String[] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.putRootDocuments(0, resources, new MtpRoot[] {
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
- database.clearMapping();
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.clearMapping();
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
});
- database.clearMapping();
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.clearMapping();
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
});
- database.resolveRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
+
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 1, cursor.getInt(0));
@@ -568,7 +591,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 1, cursor.getInt(0));
@@ -578,27 +601,31 @@
}
public void testPutSameNameRootsAfterClearing() throws Exception {
- final MtpDatabase database = new MtpDatabase(getContext());
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
- MtpDatabase.COLUMN_STORAGE_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
final String[] rootColumns = new String[] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- database.putRootDocuments(0, resources, new MtpRoot[] {
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
- database.clearMapping();
- database.putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.clearMapping();
+
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
});
- database.resolveRootDocuments(0);
+ mDatabase.stopAddingRootDocuments(0);
+
{
- final Cursor cursor = database.queryRootDocuments(columns);
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("documentId", 2, cursor.getInt(0));
@@ -611,7 +638,7 @@
cursor.close();
}
{
- final Cursor cursor = database.queryRoots(rootColumns);
+ final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
assertEquals("rootId", 2, cursor.getInt(0));
@@ -622,4 +649,69 @@
cursor.close();
}
}
+
+ public void testReplaceExistingRoots() {
+ // The client code should be able to replace existing rows with new information.
+ // Add one.
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+ });
+ mDatabase.stopAddingRootDocuments(0);
+ // Replace it.
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
+ });
+ mDatabase.stopAddingRootDocuments(0);
+ {
+ final String[] columns = new String[] {
+ DocumentsContract.Document.COLUMN_DOCUMENT_ID,
+ MtpDatabaseConstants.COLUMN_STORAGE_ID,
+ DocumentsContract.Document.COLUMN_DISPLAY_NAME
+ };
+ final Cursor cursor = mDatabase.queryRootDocuments(columns);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("documentId", 1, cursor.getInt(0));
+ assertEquals("storageId", 100, cursor.getInt(1));
+ assertEquals("name", "Device Storage B", cursor.getString(2));
+ cursor.close();
+ }
+ {
+ final String[] columns = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ final Cursor cursor = mDatabase.queryRoots(columns);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("rootId", 1, cursor.getInt(0));
+ assertEquals("availableBytes", 1000, cursor.getInt(1));
+ cursor.close();
+ }
+ }
+
+ public void _testFailToReplaceExisitingUnmappedRoots() {
+ // The client code should not be able to replace rows before resolving 'unmapped' rows.
+ // Add one.
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
+ });
+ mDatabase.clearMapping();
+ // Add one.
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
+ });
+ // Add one more before resolving unmapped documents.
+ try {
+ mDatabase.putRootDocuments(0, resources, new MtpRoot[] {
+ new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
+ });
+ fail();
+ } catch (Throwable e) {
+ assertTrue(e instanceof Error);
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 5765f0a..cabb08d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -36,13 +36,20 @@
private MtpDocumentsProvider mProvider;
private TestMtpManager mMtpManager;
private final TestResources mResources = new TestResources();
+ private MtpDatabase mDatabase;
@Override
public void setUp() throws IOException {
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
- mProvider.onCreateForTesting(mResources, mMtpManager, mResolver);
+ mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
+ }
+
+ @Override
+ public void tearDown() {
+ mProvider.shutdown();
}
public void testOpenAndCloseDevice() throws Exception {
@@ -96,26 +103,6 @@
mResolver.waitForNotification(ROOTS_URI, 1);
}
- public void testCloseAllDevices() throws Exception {
- mMtpManager.addValidDevice(0);
- mMtpManager.setRoots(0, new MtpRoot[] {
- new MtpRoot(
- 0 /* deviceId */,
- 1 /* storageId */,
- "Device A" /* device model name */,
- "Storage A" /* volume description */,
- 1024 /* free space */,
- 2048 /* total space */,
- "" /* no volume identifier */)
- });
- mProvider.closeAllDevices();
- mProvider.openDevice(0);
- mResolver.waitForNotification(ROOTS_URI, 1);
-
- mProvider.closeAllDevices();
- mResolver.waitForNotification(ROOTS_URI, 2);
- }
-
public void testQueryRoots() throws Exception {
mMtpManager.addValidDevice(0);
mMtpManager.addValidDevice(1);
@@ -146,7 +133,7 @@
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("0_1", cursor.getString(0));
+ assertEquals("1", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
@@ -162,7 +149,7 @@
assertEquals(2, cursor.getCount());
cursor.moveToNext();
cursor.moveToNext();
- assertEquals("1_1", cursor.getString(0));
+ assertEquals("2", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
@@ -170,13 +157,6 @@
assertEquals("1_1_0", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
-
- {
- mProvider.closeAllDevices();
- mResolver.waitForNotification(ROOTS_URI, 3);
- final Cursor cursor = mProvider.queryRoots(null);
- assertEquals(0, cursor.getCount());
- }
}
public void testQueryRoots_error() throws Exception {
@@ -201,7 +181,7 @@
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("1_1", cursor.getString(0));
+ assertEquals("1", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
@@ -216,6 +196,7 @@
mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
+ .setStorageId(1)
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
.setName("image.jpg")
.setDateModified(1422716400000L)
@@ -226,6 +207,7 @@
assertEquals(1, cursor.getCount());
cursor.moveToNext();
+
assertEquals("0_1_2", cursor.getString(0));
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
@@ -243,6 +225,7 @@
mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
+ .setStorageId(1)
.setFormat(MtpConstants.FORMAT_ASSOCIATION)
.setName("directory")
.setDateModified(1422716400000L)
@@ -293,6 +276,12 @@
mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
+ mDatabase.startAddingRootDocuments(0);
+ mDatabase.putRootDocuments(0, mResources, new MtpRoot[] {
+ new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
+ });
+ mDatabase.stopAddingRootDocuments(0);
+
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(1)
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index 53018cc..7c947f5 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -19,15 +19,14 @@
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
import java.io.IOException;
-import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-@SmallTest
+@MediumTest
public class PipeManagerTest extends AndroidTestCase {
private static final byte[] HELLO_BYTES = new byte[] { 'h', 'e', 'l', 'l', 'o' };
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 3d92cc2..3833799 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -96,7 +96,7 @@
if (mRoots.containsKey(deviceId)) {
return mRoots.get(deviceId);
} else {
- throw new IOException("getRoots error");
+ throw new IOException("getRoots error: " + Integer.toString(deviceId));
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
index a243375..0fb0f34 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
@@ -12,8 +12,20 @@
@Override
public void onCreate(Bundle arguments) {
+ if (arguments == null) {
+ arguments = new Bundle();
+ }
+ final boolean includeRealDeviceTest =
+ Boolean.parseBoolean(arguments.getString("realDeviceTest", "false"));
+ if (!includeRealDeviceTest) {
+ arguments.putString("notAnnotation", "com.android.mtp.RealDeviceTest");
+ }
super.onCreate(arguments);
- addTestListener(this);
+ if (includeRealDeviceTest) {
+ // Show the test result by using activity because we need to disconnect USB cable
+ // from adb host while testing with real MTP device.
+ addTestListener(this);
+ }
}
@Override
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index c7cf61a..5a6f1d1 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -61,7 +61,7 @@
<activity
android:name=".ui.PrintActivity"
- android:configChanges="orientation|screenSize"
+ android:configChanges="screenSize|smallestScreenSize|orientation"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:theme="@style/PrintActivity">
<intent-filter>
diff --git a/core/res/res/drawable/non_client_decor_title.xml b/packages/PrintSpooler/res/drawable/ic_add.xml
similarity index 67%
copy from core/res/res/drawable/non_client_decor_title.xml
copy to packages/PrintSpooler/res/drawable/ic_add.xml
index e50daea..1442b1b 100644
--- a/core/res/res/drawable/non_client_decor_title.xml
+++ b/packages/PrintSpooler/res/drawable/ic_add.xml
@@ -14,8 +14,12 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="true"
- android:drawable="@drawable/non_client_decor_title_focused" />
- <item android:drawable="@drawable/non_client_decor_title_unfocused" />
-</selector>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_search.xml b/packages/PrintSpooler/res/drawable/ic_search.xml
deleted file mode 100644
index 991fa38b..0000000
--- a/packages/PrintSpooler/res/drawable/ic_search.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item
- android:state_checked="true">
- <bitmap
- android:src="@*android:drawable/ic_menu_search"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item
- android:state_pressed="true">
- <bitmap
- android:src="@*android:drawable/ic_menu_search"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item>
- <bitmap
- android:src="@*android:drawable/ic_menu_search"
- android:tint="?android:attr/colorControlNormal">
- </bitmap>
- </item>
-
-</selector>
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 6b8aa47..a2f710d 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -69,7 +69,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ android:ellipsize="end">
</TextView>
<TextView
@@ -87,7 +89,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dip"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ android:ellipsize="end">
</TextView>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
new file mode 100644
index 0000000..11fef2d
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textIsSelectable="false"
+ android:textColor="?android:attr/textColorPrimary"
+ android:paddingStart="20dip"
+ android:paddingEnd="8dip"
+ android:minHeight="56dip"
+ android:orientation="horizontal"
+ android:text="@string/destination_default_text"
+ android:gravity="start|center_vertical" />
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index 8da5769..15cc139 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -19,7 +19,7 @@
<item
android:id="@+id/action_search"
android:title="@string/search"
- android:icon="@*android:drawable/ic_search"
+ android:icon="@*android:drawable/ic_search_api_material"
android:actionViewClass="android.widget.SearchView"
android:showAsAction="ifRoom|collapseActionView"
android:alphabeticShortcut="f"
@@ -29,7 +29,7 @@
<item
android:id="@+id/action_add_printer"
android:title="@string/print_add_printer"
- android:icon="@*android:drawable/create_contact"
+ android:icon="@drawable/ic_add"
android:showAsAction="ifRoom"
android:alphabeticShortcut="a">
</item>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 0cfa0de..f263af7 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Tweesydig"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oriëntasie"</string>
<string name="label_pages" msgid="7768589729282182230">"Bladsye"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Kies \'n drukker"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Al <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Omvang van <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"bv. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Kies drukdiens"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Soek tans vir drukkers"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Geen drukdienste is geaktiveer nie"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Geen drukkers gekry nie"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Druk tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kanselleer tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie beskikbaar nie"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Gebruik <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Jou dokument kan dalk deur een of meer bedieners op pad na die drukker gaan."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Swart en wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index 72fc67b..a93e0a9 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"ባለ ሁለት-ጎን"</string>
<string name="label_orientation" msgid="2853142581990496477">"አቀማመጠ ገፅ"</string>
<string name="label_pages" msgid="7768589729282182230">"ገፆች"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"አንድ አታሚ ይምረጡ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ሁሉም <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"የ<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ክልል"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ለምሳሌ፦ 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"የህትመት አገልግሎት ይምረጡ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"አታሚዎችን በመፈለግ ላይ"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ምንም የህትመት አገልግሎቶች አልነቁም"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ምንም አታሚዎች አልተገኙም"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በማተም ላይ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በመተው ላይ"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
<string name="reason_unknown" msgid="5507940196503246139">"አይታወቅም"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – አይገኝም"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ይጠቀሙ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ሰነድዎ ወደ አታሚው በሚሄድበት ወቅት በአንድ ወይም ከዚያ በላይ አገልጋዮች ውስጥ ሊያልፍ ይችላል።"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ጥቁር እና ነጭ"</item>
<item msgid="2762241247228983754">"ቀለም"</item>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index e28c7bd..c9a6a395 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"الجانبان"</string>
<string name="label_orientation" msgid="2853142581990496477">"الاتجاه"</string>
<string name="label_pages" msgid="7768589729282182230">"الصفحات"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"اختر طابعة"</string>
<string name="template_all_pages" msgid="3322235982020148762">"جميع الصفحات وعددها <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"النطاق <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"على سبيل المثال، 1—5،8،11—13"</string>
@@ -64,6 +65,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"اختر خدمة طباعة"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"البحث عن طابعات"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"لم يتم تمكين أي خدمات طباعة"</string>
<string name="print_no_printers" msgid="4869403323900054866">"لم يتم العثور على طابعات"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"جارٍ طباعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"جارٍ إلغاء <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,6 +84,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
<string name="reason_unknown" msgid="5507940196503246139">"غير معروف"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – غير متاحة"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"هل تريد استخدام <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"من الممكن أن يمر المستند عبر خادم أو أكثر أثناء إرساله إلى الطابعة."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"أبيض وأسود"</item>
<item msgid="2762241247228983754">"ملونة"</item>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index c102745..5aeb7bb 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"İkitərəfli"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oriyentasiya"</string>
<string name="label_pages" msgid="7768589729282182230">"Səhifələr"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Printer seçin"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Bütün <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> diapazonu"</string>
<string name="pages_range_example" msgid="8558694453556945172">"məsələn, 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Çap xidmətini seçin"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printer axtarılır"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Heç bir çap xidməti aktiv edilməyib"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Heç bir printer tapılmadı"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> çap edilir"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"naməlum"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>– əlçatmaz"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətindən istifadə edilsin?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Sənədiniz printerə qədər bir və ya daha çox server vasitəsilə keçə bilər."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Qara & Ağ"</item>
<item msgid="2762241247228983754">"Rəng"</item>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index c88d0d4..93feea5 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Двустранно"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
<string name="label_pages" msgid="7768589729282182230">"Страници"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Избиране на принтер"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Всички <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Обхват от <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"напр. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Избиране на услуга за отпечатване"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Търсене на принтери"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Няма активирани услуги за отпечатване"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Няма намерени принтери"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се отпечатва"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се анулира"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
<string name="reason_unknown" msgid="5507940196503246139">"няма данни"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – не е налице"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Да се използва ли „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"По пътя към принтера документът ви може да премине през един или повече сървъри."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Черно-бяло"</item>
<item msgid="2762241247228983754">"Цветно"</item>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index 45f20a0..0eed9aa 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"দ্বিভুজ"</string>
<string name="label_orientation" msgid="2853142581990496477">"সজ্জা"</string>
<string name="label_pages" msgid="7768589729282182230">"পৃষ্ঠাগুলি"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"একটি মুদ্রক নির্বাচন করুন"</string>
<string name="template_all_pages" msgid="3322235982020148762">"সমস্ত <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> এর পরিসর"</string>
<string name="pages_range_example" msgid="8558694453556945172">"যেমন, ১—৫,৮,১১—১৩"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"মুদ্রণ পরিষেবা চয়ন করুন"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"মুদ্রকগুলি অনুসন্ধান করা হচ্ছে"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"মুদ্রণ পরিষেবা সক্ষম নেই"</string>
<string name="print_no_printers" msgid="4869403323900054866">"কোনো মুদ্রক পাওয়া যায়নি"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> মুদ্রণ করা হচ্ছে"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> বাতিল করা হচ্ছে"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"মুদ্রকে কোনো সংযোগ নেই"</string>
<string name="reason_unknown" msgid="5507940196503246139">"অজানা"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – অনুপলব্ধ"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ব্যবহার করবেন?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"আপনার দস্তাবেজ মুদ্রকে যাওয়ার সময় এক বা একাধিক সার্ভারের মাধ্যমে পাস হতে পারে।"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"কালো এবং সাদা"</item>
<item msgid="2762241247228983754">"রঙ"</item>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 612be97..03d3060 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"De dues cares"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientació"</string>
<string name="label_pages" msgid="7768589729282182230">"Pàgines"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Tria una impressora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Totes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Interval de: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Selecció del servei d\'impressió"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Cerca d\'impressores"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No hi ha cap servei d\'impressió activat"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No s\'ha trobat cap impressora"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"S\'està imprimint <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"S\'està cancel·lant <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconegut"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Vols fer servir <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"És possible que el document passi com a mínim per un servidor abans d\'imprimir-se."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanc i negre"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 99ed75d..414abf9 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Oboustranně"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientace"</string>
<string name="label_pages" msgid="7768589729282182230">"Stránky"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Vyberte tiskárnu"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Vše: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Rozsah: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"např. 1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Zvolte službu tisku"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhledávání tiskáren"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nejsou aktivovány žádné tiskové služby"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nebyly nalezeny žádné tiskárny"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tisk úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Rušení úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznámé"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – není k dispozici"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Použít službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument může cestou do tiskárny projít jedním i více servery."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Černobíle"</item>
<item msgid="2762241247228983754">"Barevně"</item>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index 526e976..893c991 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Tosidet"</string>
<string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
<string name="label_pages" msgid="7768589729282182230">"Sider"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Vælg en printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Interval på <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"f.eks. 1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Vælg udskriftstjeneste"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Søger efter printere"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ingen udskrivningstjenester er aktiveret"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Der blev ikke fundet nogen printere"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> udskrives"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annulleres"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukendt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ikke tilgængelig"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruge <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dit dokument passerer muligvis gennem én eller flere servere på vej til printeren."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Sort/hvid"</item>
<item msgid="2762241247228983754">"Farve"</item>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index ef5e34c..f6f53ea 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Zweiseitig"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ausrichtung"</string>
<string name="label_pages" msgid="7768589729282182230">"Seiten"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Drucker auswählen"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Auswahl von <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"z. B. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Druckdienst auswählen"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Suche nach Druckern"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Keine Druckdienste aktiviert"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Keine Drucker gefunden"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird gedruckt..."</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird abgebrochen..."</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unbekannt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nicht verfügbar"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> verwenden?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Ihr Dokument passiert bei der Übermittlung an den Drucker möglicherweise einen oder mehrere Server."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Schwarz-weiß"</item>
<item msgid="2762241247228983754">"Farbe"</item>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index a4bb04c..10ddf62 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Δύο όψεων"</string>
<string name="label_orientation" msgid="2853142581990496477">"Προσανατολισμός"</string>
<string name="label_pages" msgid="7768589729282182230">"Σελίδες"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Επιλέξτε εκτυπωτή"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Και οι <xliff:g id="PAGE_COUNT">%1$s</xliff:g> σελίδες"</string>
<string name="template_page_range" msgid="428638530038286328">"Εύρος σελίδων από <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"π.χ. 1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Επιλέξτε υπηρεσία εκτύπωσης"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Αναζήτηση για εκτυπωτές"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Δεν έχουν ενεργοποιηθεί υπηρεσίες εκτύπωσης"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Δεν βρέθηκαν εκτυπωτές"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Εκτύπωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ακύρωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
<string name="reason_unknown" msgid="5507940196503246139">"άγνωστο"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – μη διαθέσιμο"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Να χρησιμοποιηθεί η υπηρεσία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Το έγγραφό σας μπορεί να περάσει από έναν ή περισσότερους διακομιστές κατά τη μετάβαση στον εκτυπωτή."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Ασπρόμαυρο"</item>
<item msgid="2762241247228983754">"Χρώμα"</item>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index 9f0d7e5..a540ac5 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Two-sided"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Select a printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"All <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Range of <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"e.g. 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 9f0d7e5..a540ac5 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Two-sided"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Select a printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"All <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Range of <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"e.g. 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 9f0d7e5..a540ac5 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Two-sided"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Select a printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"All <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Range of <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"e.g. 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index ef0dbb1..8929aa8 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Ambos lados"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
<string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Seleccionar una impresora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Todas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Rango de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"Ej.: 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Elegir servicio de impresión"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No se encontraron impresoras"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Deseas usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index 067c134..7cfd92a 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dos caras"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
<string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Elige una impresora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Todas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalo de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"p. ej.: 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Seleccionar servicio de impresión"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No se encontraron impresoras"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – no disponible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 0227131..ee93bcf 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Kahepoolne"</string>
<string name="label_orientation" msgid="2853142581990496477">"Suund"</string>
<string name="label_pages" msgid="7768589729282182230">"Lehed"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Printeri valimine"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Kõik <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Vahemik <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"nt 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Prinditeenuse valimine"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printerite otsimine"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ühtegi printimisteenust pole lubatud"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Printereid ei leitud"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> printimine"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> tühistamine"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
<string name="reason_unknown" msgid="5507940196503246139">"teadmata"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – pole saadaval"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Kas soovite kasutada teenust <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Printerini jõudmiseks võib dokument läbida ühe või mitu serverit."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Mustvalge"</item>
<item msgid="2762241247228983754">"Värv"</item>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 461a92f..882e888 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Bi aldekoa"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientazioa"</string>
<string name="label_pages" msgid="7768589729282182230">"Orriak"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Hautatu inprimagailua"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> orriak"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> orriko tartea"</string>
<string name="pages_range_example" msgid="8558694453556945172">"adib., 1-5, 8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Aukeratu inprimatze-zerbitzua"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Inprimagailuak bilatzen"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ez dago gaituta inprimatzeko zerbitzurik"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ez da inprimagailurik aurkitu"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzen"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bertan behera uzten"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Inprimagailua ez dago konektatuta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ezezaguna"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: ez dago erabilgarri"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> erabili nahi duzu?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Baliteke dokumentuak zerbitzari batean edo gehiagotan zehar igarotzea inprimagailurako bidean."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Zuri-beltza"</item>
<item msgid="2762241247228983754">"Koloretakoa"</item>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 0e1629f..10743e7 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"دوطرفه"</string>
<string name="label_orientation" msgid="2853142581990496477">"جهت"</string>
<string name="label_pages" msgid="7768589729282182230">"صفحهها"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"چاپگری انتخاب کنید"</string>
<string name="template_all_pages" msgid="3322235982020148762">"همه <xliff:g id="PAGE_COUNT">%1$s</xliff:g> صفحه"</string>
<string name="template_page_range" msgid="428638530038286328">"محدوده <xliff:g id="PAGE_COUNT">%1$s</xliff:g> صفحه"</string>
<string name="pages_range_example" msgid="8558694453556945172">"مثلاً ۱—۵،۹،۷—۱۰"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"انتخاب سرویس چاپ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"درحال جستجوی چاپگرها"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"هیچ خدمات چاپی فعال نیست"</string>
<string name="print_no_printers" msgid="4869403323900054866">"هیچ چاپگری یافت نشد"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"در حال چاپ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"در حال لغو <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - در دسترس نیست"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"از <xliff:g id="SERVICE">%1$s</xliff:g> استفاده شود؟"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ممکن است سندتان برای رسیدن به چاپگر از یک یا چند سرور عبور کند."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"سیاه و سفید"</item>
<item msgid="2762241247228983754">"رنگی"</item>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index faf40e2..ee35c41 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Kaksipuolinen"</string>
<string name="label_orientation" msgid="2853142581990496477">"Suunta"</string>
<string name="label_pages" msgid="7768589729282182230">"Sivut"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Valitse tulostin"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Kaikki <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Sivumäärä: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"esim. 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Valitse tulostuspalvelu"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Etsitään tulostimia"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ei käytössä olevia tulostuspalveluita"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tulostimia ei löydy"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tulostetaan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tuntematon"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ei käytettävissä"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Käytetäänkö palvelua <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Asiakirja saattaa kulkea yhden tai useamman palvelimen kautta matkalla tulostimeen."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Mustavalkoinen"</item>
<item msgid="2762241247228983754">"Väri"</item>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index f949bb7..eb99441 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Recto verso"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Sélectionnez une imprimante"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Toutes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Plage de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours..."</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Aucune imprimante trouvée"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »…"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnu"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — indisponible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'arriver à l\'imprimante."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 1f5c716..c0eecfb 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Recto verso"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="7768589729282182230">"Pages"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Sélect. imprimante"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Toutes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Plage de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ex. : 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Aucune imprimante trouvée"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnue"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'être envoyé sur l\'imprimante."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 0d4b040..b4a1ec6 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dual"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
<string name="label_pages" msgid="7768589729282182230">"Páxinas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Escoller impresora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"As <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalo de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ex.: 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Escoller servizo de impresión"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Busca de impresoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Non hai servizos de impresión activados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Non se atopou ningunha impresora"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Non hai conexión coa impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"descoñecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: non dispoñible"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Queres usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"É posible que o teu documento pase por un ou máis servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Branco e negro"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 28ffc2a..8f77953 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"દ્વિભુજ"</string>
<string name="label_orientation" msgid="2853142581990496477">"ઓરિએન્ટેશન"</string>
<string name="label_pages" msgid="7768589729282182230">"પૃષ્ઠો"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"પ્રિન્ટર પસંદ કરો"</string>
<string name="template_all_pages" msgid="3322235982020148762">"તમામ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ની શ્રેણી"</string>
<string name="pages_range_example" msgid="8558694453556945172">"દા.ત. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"પ્રિન્ટ સેવા પસંદ કરો"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર્સ માટે શોધી રહ્યું છે"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"કોઈ છાપ સેવાઓ સક્ષમ કરેલ નથી"</string>
<string name="print_no_printers" msgid="4869403323900054866">"કોઈ પ્રિન્ટર મળ્યા નથી"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> છાપી રહ્યાં છે"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ને રદ કરી રહ્યું છે"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"પ્રિન્ટર માટે કોઈ કનેક્શન નથી"</string>
<string name="reason_unknown" msgid="5507940196503246139">"અજાણ્યું"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – અનુપલબ્ધ"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> નો ઉપયોગ કરીએ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"તમારો દસ્તાવેજ પ્રિન્ટર સુધીના તેના માર્ગમાં એક અથવા વધુ સર્વર્સથી પસાર થઈ શકે છે."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"શ્યામ અને શ્વેત"</item>
<item msgid="2762241247228983754">"રંગ"</item>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 1a0f7d1..4c11323 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"दो-तरफ़ा"</string>
<string name="label_orientation" msgid="2853142581990496477">"अभिविन्यास"</string>
<string name="label_pages" msgid="7768589729282182230">"पृष्ठ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"कोई प्रिंटर चुनें"</string>
<string name="template_all_pages" msgid="3322235982020148762">"सभी <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"पृष्ठ संख्या <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"उदा. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"प्रिंट सेवा चुनें"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर खोज रहा है"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"कोई भी प्रिंट सेवा सक्षम नहीं है"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कोई प्रिंटर नहीं मिले"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> प्रिंट हो रहा है"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द हो रहा है"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> का उपयोग करें?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"प्रिंटर पर जाते समय आपका दस्तावेज़ एक या अधिक सर्वर से गुज़र सकता है."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"श्याम और श्वेत"</item>
<item msgid="2762241247228983754">"रंग"</item>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 3e38d65..4cec3ba 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Obostrano"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orijentacija"</string>
<string name="label_pages" msgid="7768589729282182230">"Stranice"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Odaberite pisač"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Sve stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Raspon od <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"npr. 1 – 5,8,11 – 13"</string>
@@ -61,6 +62,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Odaberite uslugu ispisa"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Traženje pisača"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nije omogućena nijedna usluga ispisa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nije pronađen nijedan pisač"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Ispisivanje <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje zadatka <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -76,6 +78,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – zadatak nije dostupan"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li upotrijebiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Na putu do pisača vaš dokument može proći kroz jedan ili više poslužitelja."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Crno-bijelo"</item>
<item msgid="2762241247228983754">"U boji"</item>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 2b24b1f..ac1ba6e 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Kétoldalas"</string>
<string name="label_orientation" msgid="2853142581990496477">"Tájolás"</string>
<string name="label_pages" msgid="7768589729282182230">"Oldalak"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Válasszon ki egy nyomtatót"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Összes (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> oldalas tartomány"</string>
<string name="pages_range_example" msgid="8558694453556945172">"pl. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Nyomtatási szolgáltatás kiválasztása"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Nyomtatók keresése"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nincs engedélyezett nyomtatási szolgáltatás"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nem található nyomtató"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> nyomtatása"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> törlése"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ismeretlen"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nem érhető el"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Használni szeretné a következő szolgáltatást: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"A dokumentum áthaladhat egy vagy több szerveren, mielőtt a nyomtatóhoz érne."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Fekete-fehér"</item>
<item msgid="2762241247228983754">"Szín"</item>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 56f94b4..dda6745 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Երկկողմանի"</string>
<string name="label_orientation" msgid="2853142581990496477">"Դիրքավորում"</string>
<string name="label_pages" msgid="7768589729282182230">"Էջեր"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Ընտրել տպիչ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Բոլորը՝ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Միջակայքը՝ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"օր.՝ 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Ընտրեք տպելու ծառայությունը"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Տպիչների որոնում"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ակտիվացված տպման ծառայություններ չկան"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Տպիչներ չեն գտնվել"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Տպվում է՝ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը չեղարկվում է"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
<string name="reason_unknown" msgid="5507940196503246139">"անհայտ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> տպիչն անհասանելի է"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Օգտագործե՞լ <xliff:g id="SERVICE">%1$s</xliff:g>-ը:"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Հնարավոր է՝ փաստաթուղթը մի քանի սերվերներով անցնի մինչ տպվելը:"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Սև ու սպիտակ"</item>
<item msgid="2762241247228983754">"Գույնը"</item>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index d09fcfe..b203e2b 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Bersisi ganda"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
<string name="label_pages" msgid="7768589729282182230">"Halaman"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Pilih printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Semua dari <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Rentang dari <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"misalnya 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Pilih layanan cetak"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari printer"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Tidak ada layanan cetak yang aktif"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tidak ditemukan printer"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tak diketahui"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen Anda dapat melewati satu atau beberapa server saat menuju printer."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index bf13ebf..6dfdabc 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Tvíhliða"</string>
<string name="label_orientation" msgid="2853142581990496477">"Stefna"</string>
<string name="label_pages" msgid="7768589729282182230">"Síður"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Veldu prentara"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Allar <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"t.d. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Veldu prentþjónustu"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Leitar að prentara"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Engin prentþjónusta er virk"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Engir prentarar fundust"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prentar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hættir við <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Engin tenging við prentara"</string>
<string name="reason_unknown" msgid="5507940196503246139">"óþekkt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ekki í boði"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Nota <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skjalið gæti þurft að fara í gegnum einn eða fleiri þjóna á leið sinni til prentarans."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Svarthvítt"</item>
<item msgid="2762241247228983754">"Í lit"</item>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index b052341..fd5473a 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Con doppia funzione"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientamento"</string>
<string name="label_pages" msgid="7768589729282182230">"Pagine"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Seleziona stampante"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Tutte e <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervallo di <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"Es.: 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Scegli servizio di stampa"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Ricerca di stampanti"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Non è stato attivato alcun servizio di stampa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nessuna stampante trovata"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Stampa di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"sconosciuto"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - non disponibile"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Utilizzare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Il tuo documento potrebbe passare da uno o più server per raggiungere la stampante."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Bianco e nero"</item>
<item msgid="2762241247228983754">"A colori"</item>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index bb1967d..dd062a3 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"דו-צדדי"</string>
<string name="label_orientation" msgid="2853142581990496477">"כיוון"</string>
<string name="label_pages" msgid="7768589729282182230">"עמודים"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"בחר מדפסת"</string>
<string name="template_all_pages" msgid="3322235982020148762">"הכל <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"טווח של <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"למשל 1–5,8,11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"בחר שירות הדפסה"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"מחפש מדפסות"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"לא הופעלו שירותי הדפסה"</string>
<string name="print_no_printers" msgid="4869403323900054866">"לא נמצאו מדפסות"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"מדפיס את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"מבטל את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
<string name="reason_unknown" msgid="5507940196503246139">"לא ידוע"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – לא זמינה"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"האם להשתמש ב-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ייתכן שהמסמך שלך יעבור בשרת אחד או יותר בדרכו למדפסת."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"שחור ולבן"</item>
<item msgid="2762241247228983754">"צבע"</item>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index 8ef7517..23e4809 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"両面"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="7768589729282182230">"ページ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"プリンタを選択"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>ページすべて"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>ページ分"</string>
<string name="pages_range_example" msgid="8558694453556945172">"例: 1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"印刷サービスの選択"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"プリンタの検索中"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"使用できる印刷サービスがありません"</string>
<string name="print_no_printers" msgid="4869403323900054866">"プリンタが見つかりません"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>を印刷しています"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をキャンセルしています"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>–使用不可"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>を利用しますか?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ドキュメントは1つ以上のサーバーを経由してプリンタに送信されることがあります。"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"モノクロ"</item>
<item msgid="2762241247228983754">"カラー"</item>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index b65d013..9f86f05 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"ორმხრივი"</string>
<string name="label_orientation" msgid="2853142581990496477">"ორიენტაცია"</string>
<string name="label_pages" msgid="7768589729282182230">"გვერდები"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"პრინტერის არჩევა"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ყველა <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>-ის არეალი"</string>
<string name="pages_range_example" msgid="8558694453556945172">"მაგ. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"აირჩიეთ ბეჭდვის სერვისი"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"მიმდინარეობს პრინტერების ძიება"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ბეჭდვის სერვისები გააქტიურებული არ არის"</string>
<string name="print_no_printers" msgid="4869403323900054866">"პრინტერები ვერ მოიძებნა"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"იბეჭდება <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"მიმდინარეობს <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ის გაუქმება"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
<string name="reason_unknown" msgid="5507940196503246139">"უცნობი"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – მიუწვდომელია"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"გსურთ, გამოიყენოთ <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"პრინტერამდე გზად დოკუმენტმა შეიძლება ერთი ან მეტი სერვერი გაიაროს."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"შავ-თეთრი"</item>
<item msgid="2762241247228983754">"ფერი"</item>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index ef914f0c..05c300e 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Екі жақты"</string>
<string name="label_orientation" msgid="2853142581990496477">"Бағыты"</string>
<string name="label_pages" msgid="7768589729282182230">"Беттер"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Принтерді таңдау"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Барлық <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ауқымы"</string>
<string name="pages_range_example" msgid="8558694453556945172">"мысалы, 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Принтер қызметін таңдау"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлерді іздеу"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Басып шығару қызметтері қосылмаған"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ешқандай принтер табылмады"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басып шығарылуда"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын тоқтатуда"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтермен байланыс жоқ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгісіз"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – қол жетімсіз"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> қолданылсын ба?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Құжат принтерге жеткенше бір немесе бірнеше серверден өтуі мүмкін."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Қара & Ақ"</item>
<item msgid="2762241247228983754">"Түс"</item>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index 3aac97f..0861e59 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"សងខាង"</string>
<string name="label_orientation" msgid="2853142581990496477">"ទិស"</string>
<string name="label_pages" msgid="7768589729282182230">"ទំព័រ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ជ្រើសម៉ាស៊ីនបោះពុម្ព"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ទាំងអស់"</string>
<string name="template_page_range" msgid="428638530038286328">"ជួរនៃ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ឧ. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"ជ្រើសសេវាបោះពុម្ព"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ស្វែងរកម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"គ្មានការបើកដំណើរការសេវាបោះពុម្ពទេ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"រកមិនឃើញម៉ាស៊ីនបោះពុម្ព"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"កំពុងបោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"ការបោះបង់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"ប្រើ <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ឯកសាររបស់អ្នកអាចនឹងឆ្លងកាត់ម៉ាស៊ីនមេមួយ ឬច្រើននៅពេលដែលវាធ្វើដំណើរទៅកាន់ម៉ាស៊ីនបោះពុម្ព។"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ស & ខ្មៅ"</item>
<item msgid="2762241247228983754">"ពណ៌"</item>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index 72e0bac..71b098d 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"ಎರಡು ಬದಿ"</string>
<string name="label_orientation" msgid="2853142581990496477">"ಓರಿಯಂಟೇಶನ್"</string>
<string name="label_pages" msgid="7768589729282182230">"ಪುಟಗಳು"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ಪ್ರಿಂಟರ್ ಆಯ್ಕೆ ಮಾಡಿ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ಎಲ್ಲಾ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ನ ಶ್ರೇಣಿ"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ಉದಾ. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"ಮುದ್ರಣ ಸೇವೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ಪ್ರಿಂಟರ್ಗಳಿಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ಯಾವುದೇ ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ಯಾವುದೇ ಮುದ್ರಕಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ಮುದ್ರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ರದ್ದು ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ಅಜ್ಞಾತ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ಬಳಸುವುದೇ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ನಿಮ್ಮ ಡಾಕ್ಯುಮೆಂಟ್ ಪ್ರಿಂಟರ್ಗೆ ಹೋಗುವ ಸಂದರ್ಭದಲ್ಲಿ ಒಂದು ಅಥವಾ ಅದಕ್ಕಿಂತ ಹೆಚ್ಚು ಸರ್ವರ್ಗಳ ಮೂಲಕ ಹಾದು ಹೋಗಬಹುದು."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ಕಪ್ಪು & ಬಿಳುಪು"</item>
<item msgid="2762241247228983754">"ಬಣ್ಣ"</item>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 0d9a4dd..451ab58 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"양면"</string>
<string name="label_orientation" msgid="2853142581990496477">"방향"</string>
<string name="label_pages" msgid="7768589729282182230">"페이지"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"프린터 선택"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>페이지 모두"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>페이지 범위"</string>
<string name="pages_range_example" msgid="8558694453556945172">"예: 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"인쇄 서비스 선택"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"프린터 검색 중"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"사용 가능한 프린트 서비스 없음"</string>
<string name="print_no_printers" msgid="4869403323900054866">"프린터 없음"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 인쇄 중"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 취소 중"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
<string name="reason_unknown" msgid="5507940196503246139">"알 수 없음"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 사용할 수 없음"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용할까요?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"문서가 프린터로 전송되는 중에 하나 이상의 서버를 통과할 수 있습니다."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"흑백"</item>
<item msgid="2762241247228983754">"컬러"</item>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 7f6eb10..98da08c 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Эки тараптуу"</string>
<string name="label_orientation" msgid="2853142581990496477">"Багыттоо"</string>
<string name="label_pages" msgid="7768589729282182230">"Баракчалар"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Принтер тандаңыз"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Бардыгы <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> аралыгы"</string>
<string name="pages_range_example" msgid="8558694453556945172">"мис. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Принтер кызматын тандоо"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлер изделүүдө"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Принтер-кызматтары иштетилген эмес"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтерлер табылган жок"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басылууда"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> токтотулууда"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер менен байланыш жок"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгисиз"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – жеткиликтүү эмес"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> колдонулсунбу?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Принтерге жеткиче документиңиз бир же андан көп серверлерден өтүшү мүмкүн."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Кара-ак"</item>
<item msgid="2762241247228983754">"Түстүү"</item>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 7e65b72..2029fdf 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"ສອງດ້ານ"</string>
<string name="label_orientation" msgid="2853142581990496477">"ລວງ"</string>
<string name="label_pages" msgid="7768589729282182230">"ໜ້າ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ເລືອກເຄື່ອງພິມ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ທັງໝົດ <xliff:g id="PAGE_COUNT">%1$s</xliff:g> ໜ້າ"</string>
<string name="template_page_range" msgid="428638530038286328">"ໄລຍະ <xliff:g id="PAGE_COUNT">%1$s</xliff:g> ໜ້າ"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ຕົວຢ່າງ: 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"ເລືອກບໍລິການການພິມ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ກຳລັງຊອກຫາເຄື່ອງພິມ"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ບໍ່ມີການບໍລິການພິມເປີດໃຊ້ງານໄວ້"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ບໍ່ພົບເຄື່ອງພິມ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"ກຳລັງພິມ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"ກຳລັງຍົກເລີກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ບໍ່ຮູ້ຈັກ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - ບໍ່ມີຢູ່"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"ໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ເອກະສານຂອງທ່ານອາດເດີນທາງຜ່ານໜຶ່ງ ຫຼື ຫຼາຍເຊີບເວີ ເພື່ອໄປຮອດເຄື່ອງພິມ."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ຂາວດຳ"</item>
<item msgid="2762241247228983754">"ສີ"</item>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index a3129cc..972abb5 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dvipusis"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientacija"</string>
<string name="label_pages" msgid="7768589729282182230">"Puslapiai"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Spausdint. pasirink."</string>
<string name="template_all_pages" msgid="3322235982020148762">"Visi <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Diapazonas: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"pvz., 1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Pasirinkite spausdinimo paslaugą"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Ieškoma spausdintuvų"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Neįgalinta jokių spausdinimo paslaugų"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nerasta spausdintuvų"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Spausdinama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Atšaukiama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nežinoma"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ – nepasiekiama"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Naudoti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Kai dokumentas siunčiamas į spausdintuvą, jis gali būti perduodamas per vieną ar daugiau serverių."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Nespalvotas"</item>
<item msgid="2762241247228983754">"Spalva"</item>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index daadf99..f565b23 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Divpusējs"</string>
<string name="label_orientation" msgid="2853142581990496477">"Virziens"</string>
<string name="label_pages" msgid="7768589729282182230">"Lapas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Atlasīt printeri"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Visas <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Diapazons: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"piem., 1–5,8,11–13"</string>
@@ -61,6 +62,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Izvēlieties drukāšanas pakalpojumu"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printeru meklēšana"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nav iespējots neviens drukas pakalpojums"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Netika atrasts neviens printeris."</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Notiek darba <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> drukāšana…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Pārtrauc drukas darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
@@ -76,6 +78,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nezināms"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — nav pieejams"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Vai izmantot pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokuments, iespējams, tiek pārsūtīts caur vienu vai vairākiem serveriem, līdz tas nonāk līdz printerim."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Melnbalts"</item>
<item msgid="2762241247228983754">"Krāsa"</item>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index 3ebc1c9..f5c06d1 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Двостран"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентација"</string>
<string name="label_pages" msgid="7768589729282182230">"Страници"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Избери печатач"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Сите <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Опсег од <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"на пр.: 1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Избери услуга печатење"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Пребарување печатачи"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Нема овозможени услуги за печатење"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Не се пронајдени печатачи"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се печати"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се откажува"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема поврзување со печатач"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - недостапен"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Користи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"На пат до печатачот, документот може да помине преку еден или повеќе сервери."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Црно-бела"</item>
<item msgid="2762241247228983754">"Во боја"</item>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index 63b4060..2d45ce5 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"രണ്ട് വശങ്ങളുള്ളത്"</string>
<string name="label_orientation" msgid="2853142581990496477">"ഓറിയന്റേഷന്"</string>
<string name="label_pages" msgid="7768589729282182230">"പേജുകൾ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ഒരു പ്രിന്റർ തിരഞ്ഞെടുക്കുക"</string>
<string name="template_all_pages" msgid="3322235982020148762">"എല്ലാ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> എന്നതിന്റെ പരിധി"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ഉദാ. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"പ്രിന്റ് സേവനം തിരഞ്ഞെടുക്കുക"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"പ്രിന്ററുകൾക്കായി തിരയുന്നു"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"പ്രിന്റ് സേവനങ്ങളൊന്നും പ്രവർത്തനക്ഷമാക്കിയിട്ടില്ല"</string>
<string name="print_no_printers" msgid="4869403323900054866">"പ്രിന്ററുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> പ്രിന്റുചെയ്യുന്നു"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> റദ്ദാക്കുന്നു"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"പ്രിന്ററിൽ കണക്ഷനൊന്നുമില്ല"</string>
<string name="reason_unknown" msgid="5507940196503246139">"അജ്ഞാതം"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ലഭ്യമല്ല"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ഉപയോഗിക്കണോ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"നിങ്ങളുടെ പ്രമാണം പ്രിന്ററിലേക്ക് പോകുന്നതിനിടെ അത് ഒന്നോ അതിലധികമോ സെർവറുകളിലൂടെ കടന്നുപോകാനിടയുണ്ട്."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"കറുപ്പ് & വെള്ള"</item>
<item msgid="2762241247228983754">"നിറം"</item>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index a8628c9..f2c7b73 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Хоёр талт"</string>
<string name="label_orientation" msgid="2853142581990496477">"Чиглэл"</string>
<string name="label_pages" msgid="7768589729282182230">"Хуудас"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Хэвлэгчийг сонгоно уу"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Нийт <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Хүрээ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ж.нь. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Хэвлэх үйлчилгээг сонгох"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтер хайж байна"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Хэвлэх үйлчилгээг идэвхжүүлээгүй"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтер олдсонгүй"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Хэвлэж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Цуцлаж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
<string name="reason_unknown" msgid="5507940196503246139">"тодорхойгүй"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ашиглах боломжгүй"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>-г ашиглах уу?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Таны документ хэвлэгчид иртэл нэг эсвэл хэд хэдэн серверээр дамжина."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Хар & Цагаан"</item>
<item msgid="2762241247228983754">"Өнгө"</item>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index c08e440..1c079dc 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"दोन्ही बाजूंनी"</string>
<string name="label_orientation" msgid="2853142581990496477">"अभिमुखता"</string>
<string name="label_pages" msgid="7768589729282182230">"पृष्ठे"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"प्रिंटर निवडा"</string>
<string name="template_all_pages" msgid="3322235982020148762">"सर्व <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ची श्रेणी"</string>
<string name="pages_range_example" msgid="8558694453556945172">"उदा. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"मुद्रण सेवा निवडा"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही मुद्रण सेवा सक्षम केलेल्या नाहीत"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कोणतेही प्रिंटर आढळले नाही"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> मुद्रण करीत आहे"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करीत आहे"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्शन नाही"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> वापरायची?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"आपला दस्तऐवज प्रिंटरपर्यंत पोहचण्यापूर्वी एक किंवा अधिक सर्व्हरद्वारे जाऊ शकतो."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"कृष्ण धवल"</item>
<item msgid="2762241247228983754">"रंग"</item>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 4ed1019..d6b5ea7 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dua sisi"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
<string name="label_pages" msgid="7768589729282182230">"Halaman"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Pilih pencetak"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Semua <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Julat <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"cth. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Pilih perkhidmatan cetak"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari pencetak"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Perkhidmatan cetak tidak didayakan"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tiada pencetak ditemui"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tidak diketahui"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen anda mungkin melalui satu atau beberapa pelayan dalam perjalanan ke pencetak."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index 0a65a11..c3dc490 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"နှစ်ဖက်လှ"</string>
<string name="label_orientation" msgid="2853142581990496477">"အနေအထား"</string>
<string name="label_pages" msgid="7768589729282182230">"စာမျက်နှာများ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ပုံနှိပ်စက်ကို ရွေးပါ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"အားလုံး <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>ဘောင် ထဲမှာ"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ဥပမာ ၁-၅၊ ၈၊ ၁၁-၁၃"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"စာထုတ်ရန် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"စာထုတ်စက်များကို ရှာနေပါသည်"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ပုံနှိပ်ထုတ်ယူရေး ဝန်ဆောင်မှုများ ဖွင့်မထားပါ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"စာထုတ်စက် တစ်ခုမှ မတွေ့ရှိပါ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို စာထုတ်နေပါသည်"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"အကြောင်းအရာ မသိရှိ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – မတွေ့ရှိပါ"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ကိုသုံးမလား။"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"သင်၏ စာရွက်စာတမ်းများသည် ပရင်တာထံသို့ သွားစဉ် ဆာဗာ တစ်ခု သို့မဟုတ် ပိုများပြီး ဖြတ်ကျော်နိုင်ရသည်။"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"အဖြူ အမည်း"</item>
<item msgid="2762241247228983754">"ရောင်စုံ"</item>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 7c74b39..945bbea 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Tosidig"</string>
<string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
<string name="label_pages" msgid="7768589729282182230">"Sider"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Velg en skriver"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Område på <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"f.eks. 1–5, 8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Velg utskriftstjeneste"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Søker etter skrivere"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ingen utskriftstjenester er slått på"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Fant ingen skrivere"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukjent"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – utilgjengelig"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruke <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumentet ditt kan gå via flere tjenere før det når skriveren."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Svart og hvitt"</item>
<item msgid="2762241247228983754">"Farge"</item>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 42cd98b..45bcc95 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"दुई-पक्षीय"</string>
<string name="label_orientation" msgid="2853142581990496477">"अभिमुखिकरण"</string>
<string name="label_pages" msgid="7768589729282182230">"पृष्ठहरू"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"कुनै मुद्रक चयन गर्नुहोस्"</string>
<string name="template_all_pages" msgid="3322235982020148762">"सबै <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> को सीमा"</string>
<string name="pages_range_example" msgid="8558694453556945172">"उदाहरण १-५,८,११-१३"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"प्रिन्ट सेवा छनौट गर्नुहोस्"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोज्दै"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"कुनै पनि मुद्रण सेवाहरू सक्रिय छैनन्"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कुनै प्रिन्टरहरू भेटाइएन"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"प्रिन्ट गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"रद्द गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिन्टरमा कुनै जडान छैन"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - अनुपलब्ध"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"तपाईँको कागजात प्रिन्टरमा जाँदा यसको मार्गमा एक वा धेरै सर्भरहरू पार हुनसक्छन्।"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"कालो & सेतो"</item>
<item msgid="2762241247228983754">"रङ्ग"</item>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 4855e31..76c8656 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dubbelzijdig"</string>
<string name="label_orientation" msgid="2853142581990496477">"Stand"</string>
<string name="label_pages" msgid="7768589729282182230">"Pagina\'s"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Printer selecteren"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Alle <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Bereik van <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"bijv. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Afdrukservice kiezen"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printers zoeken"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Geen afdrukservices ingeschakeld"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Geen printers gevonden"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> afdrukken"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annuleren"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niet beschikbaar"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> gebruiken?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Je document kan via een of meer servers naar de printer worden verzonden."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Zwart-wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 912cf4a..45fa460 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"ਦੋ-ਪਾਸੇ ਦਾ"</string>
<string name="label_orientation" msgid="2853142581990496477">"ਅਨੁਕੂਲਨ"</string>
<string name="label_pages" msgid="7768589729282182230">"ਸਫ਼ੇ"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ਇੱਕ ਪ੍ਰਿੰਟਰ ਚੁਣੋ"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ਸਾਰੇ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ਦੀ ਰੇਂਜ"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ਉਦਾਹਰਨ ਲਈ 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"ਪ੍ਰਿੰਟ ਸੇਵਾ ਚੁਣੋ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜ ਰਿਹਾ ਹੈ"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਯੋਗ ਨਹੀਂ ਬਣਾਈਆਂ ਗਈਆਂ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ਕੋਈ ਪ੍ਰਿੰਟਰ ਨਹੀਂ ਮਿਲੇ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਿੰਟ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਰੱਦ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ਪ੍ਰਿੰਟਰ ਲਈ ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ਅਗਿਆਤ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ਅਣਉਪਲਬਧ"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਵਰਤਣੀ ਹੈ?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ਤੁਹਾਡਾ ਦਸਤਾਵੇਜ਼ ਪ੍ਰਿੰਟਰ ਵਿੱਚ ਜਾਣ ਲਈ ਇੱਕ ਜਾਂ ਦੋ ਸਰਵਰਾਂ ਵਿੱਚੋਂ ਲੰਘਦਾ ਹੈ।"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ਕਾਲਾ & ਚਿੱਟਾ"</item>
<item msgid="2762241247228983754">"ਰੰਗ"</item>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 055fb5f..df3ee924 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dwustronny"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientacja"</string>
<string name="label_pages" msgid="7768589729282182230">"Strony"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Wybierz drukarkę"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Wszystkie <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Zakres <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"np. 1-5, 8, 11-13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Wybierz usługę drukowania"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Szukanie drukarek"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Brak włączonych usług drukowania"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nie znaleziono drukarek"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Drukowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Anulowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
<string name="reason_unknown" msgid="5507940196503246139">"brak informacji"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niedostępne"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Użyć usługi <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Zanim dokument dotrze do drukarki, może przejść przez jeden lub kilka serwerów."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Czarno-białe"</item>
<item msgid="2762241247228983754">"Kolor"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 9e38506..90da72b 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dois lados"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
<string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Selec. impressora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Todas as <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalo de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"Ex.: 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 73fabab..99bbd81 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dois lados"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
<string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Selec. impressora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Todas as <xliff:g id="PAGE_COUNT">%1$s</xliff:g> páginas"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalo de <xliff:g id="PAGE_COUNT">%1$s</xliff:g> pág."</string>
<string name="pages_range_example" msgid="8558694453556945172">"p. ex. 1-5, 8, 11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Escolher o serviço de impressão"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"A procurar impressoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"A imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponível"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Pretende utilizar o <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"O seu documento pode passar por um ou mais servidores no respetivo caminho para a impressora."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 9e38506..90da72b 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dois lados"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
<string name="label_pages" msgid="7768589729282182230">"Páginas"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Selec. impressora"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Todas as <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalo de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"Ex.: 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 3ff1045..4cfb8ab 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Față-verso"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientare"</string>
<string name="label_pages" msgid="7768589729282182230">"Pagini"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Selectați imprimanta"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Toate cele <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalul de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"de ex. 1-5, 8, 11-13"</string>
@@ -61,6 +62,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Alegeți serviciul de printare"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Se caută imprimante"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Niciun serviciu de printare activat"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nu au fost găsite imprimante"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Se printează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -76,6 +78,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
<string name="reason_unknown" msgid="5507940196503246139">"necunoscut"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - indisponibil"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Folosiți <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Documentul poate trece prin unul sau mai multe servere pe calea spre imprimantă."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Alb-negru"</item>
<item msgid="2762241247228983754">"Color"</item>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index a4e12fe..fb49330 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Двусторонний"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
<string name="label_pages" msgid="7768589729282182230">"Страницы"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Выберите принтер"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Все <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Диапазон <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"напр., 1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Выберите службу печати"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Поиск принтеров…"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Службы печати недоступны"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ничего не найдено"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Печать задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\"…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отмена задания <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"неизвестно"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступен"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Использовать <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ может пересылаться на принтер через несколько серверов."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Черно-белая"</item>
<item msgid="2762241247228983754">"Цветная"</item>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index e8052d6..fb6f145 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"පැති-දෙකක"</string>
<string name="label_orientation" msgid="2853142581990496477">"දිශානතිය"</string>
<string name="label_pages" msgid="7768589729282182230">"පිටු"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"මුද්රණ යන්ත්රයක් තෝරන්න"</string>
<string name="template_all_pages" msgid="3322235982020148762">"සියලුම <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> පරාසය"</string>
<string name="pages_range_example" msgid="8558694453556945172">"උ.දා. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"මුද්රණ සේවාව තෝරන්න"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"මුද්රණ යන්ත්ර සොයමින්"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"මුද්රණ සේවා සබල නැත"</string>
<string name="print_no_printers" msgid="4869403323900054866">"මුද්රණ යන්ත්ර සොයා නොගැනුණි"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> මුද්රණය වේ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"අවලංගු කෙරේ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"මුද්රණ යන්ත්රය වෙත සම්බන්ධය නැත"</string>
<string name="reason_unknown" msgid="5507940196503246139">"නොදනී"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ලද නොහැක"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> භාවිත කරන්නද?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"ඔබගේ ලේඛනය මුද්රණ යන්ත්රයට යන අතරතුර සේවාදායක එකක් හෝ කිහිපයක් හරහා යා හැක."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"කළු සහ සුදු"</item>
<item msgid="2762241247228983754">"වර්ණය"</item>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 3e82076..605237b 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Obojstranné"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientácia"</string>
<string name="label_pages" msgid="7768589729282182230">"Strany"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Výber tlačiarne"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Všetky: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Rozsah: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"napr. 1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Výber tlačovej služby"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhľadávanie tlačiarní"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Žiadne tlačové služby nie sú aktivované"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenašli sa žiadne tlačiarne"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prebieha tlač úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prebieha zrušenie úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznáme"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie je k dispozícii"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Použiť službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skôr ako sa váš dokument dostane do tlačiarne, môže prejsť jedným alebo viacerými servermi."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Čiernobiele"</item>
<item msgid="2762241247228983754">"Farba"</item>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 75c2abe..48d2e1d 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dvostransko"</string>
<string name="label_orientation" msgid="2853142581990496477">"Postavitev"</string>
<string name="label_pages" msgid="7768589729282182230">"Strani"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Izberite tiskalnik"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Vse (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Obseg strani: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"npr. 1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Izberite tiskalno storitev"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Iskanje tiskalnikov"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ni omogočenih tiskalnih storitev"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tiskalnikov ni mogoče najti"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tiskanje: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Preklic: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznano"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ni na voljo"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite uporabiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument gre lahko na poti do tiskalnika skozi enega ali več strežnikov."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Črno-belo"</item>
<item msgid="2762241247228983754">"Barvno"</item>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index adbf700..5ba72ff 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Në dy anë"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientimi"</string>
<string name="label_pages" msgid="7768589729282182230">"Faqe"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Zgjidh një printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Të <xliff:g id="PAGE_COUNT">%1$s</xliff:g> faqet"</string>
<string name="template_page_range" msgid="428638530038286328">"Gama e faqeve: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"p.sh. 1 - 5,8,11 - 13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Zgjidh shërbimin e printimit"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Po kërkon për printerë"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nuk ka shërbime printimi të aktivizuara"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nuk u gjet asnjë printer"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Po printon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Po anulon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeri nuk është i lidhur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"e panjohur"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nuk mundësohet"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Përdor <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumenti mund të kalojë përmes një ose shumë serverëve deri te printeri."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Bardhezi"</item>
<item msgid="2762241247228983754">"Ngjyra"</item>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 58e9622..7a04b8d 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Двострано"</string>
<string name="label_orientation" msgid="2853142581990496477">"Положај"</string>
<string name="label_pages" msgid="7768589729282182230">"Странице"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Изаберите штампач"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Све странице (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"Опсег од <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"нпр. 1–5, 8, 11–13"</string>
@@ -61,6 +62,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Изаберите услугу штампања"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Претрага штампача"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Ниједна услуга штампања није омогућена"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Није пронађен ниједан штампач"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Штампа се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отказује се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -76,6 +78,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступан"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Желите ли да користите <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ може да прође кроз један или више сервера на путу до штампача."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Црно-бело"</item>
<item msgid="2762241247228983754">"Боја"</item>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 0434903..ec4ad30 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Dubbelsidig"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientering"</string>
<string name="label_pages" msgid="7768589729282182230">"Sidor"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Välj skrivare"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Alla <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervall på <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"t.ex. 1–5,8,11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Välj utskriftstjänst"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Söker efter skrivare"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Inga utskriftstjänster har aktiverats"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Det gick inte att hitta några skrivare"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"okänt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – inte tillgänglig"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Vill du använda <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"På vägen till skrivaren kan dokumentet passera en eller flera servrar."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Svartvit"</item>
<item msgid="2762241247228983754">"Färg"</item>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 7c08316..eed3356 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Yenye pande mbili"</string>
<string name="label_orientation" msgid="2853142581990496477">"Mkao"</string>
<string name="label_pages" msgid="7768589729282182230">"Kurasa"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Chagua printa"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Kurasa zote <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Mfululizo wa <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"k.m. 1–5, 8, 11–13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Chagua huduma ya printa"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Inatafuta printa"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Huduma za kuchapisha hazijawashwa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Hakuna printa zilizopatikana"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Inachapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Inaghairi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
<string name="reason_unknown" msgid="5507940196503246139">"haijulikani"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - haipatikani"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Ungependa kutumia <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Huenda hati yako ikapitia seva moja au zaidi kabla ya kufika kwenye printa."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Nyeusi na Nyeupe"</item>
<item msgid="2762241247228983754">"Rangi"</item>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 4c9f135..a9879c3 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"இரு பக்க முறை"</string>
<string name="label_orientation" msgid="2853142581990496477">"திசையமைப்பு"</string>
<string name="label_pages" msgid="7768589729282182230">"பக்கங்கள்"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"பிரிண்டரை தேர்ந்தெடு"</string>
<string name="template_all_pages" msgid="3322235982020148762">"எல்லாம்: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"வரம்பில்: <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"எ.கா. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"அச்சுப் பொறியைத் தேர்வுசெய்யவும்"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"அச்சுப்பொறிகளைத் தேடுகிறது"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string>
<string name="print_no_printers" msgid="4869403323900054866">"பிரிண்டர்கள் எதுவுமில்லை"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ அச்சிடுகிறது"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ ரத்துசெய்கிறது"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"அச்சுப்பொறியுடன் இணைக்கப்படவில்லை"</string>
<string name="reason_unknown" msgid="5507940196503246139">"அறியப்படாதது"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – இல்லை"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"உங்கள் ஆவணம் பிரிண்டருக்குச் செல்லும் வழியில் ஒன்று அல்லது அதற்கு மேற்பட்ட சேவையகங்களைக் கடந்து செல்லக்கூடும்."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"கருப்பு & வெள்ளை"</item>
<item msgid="2762241247228983754">"வண்ணம்"</item>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index ffa994b..909cb90 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"రెండు వైపుల"</string>
<string name="label_orientation" msgid="2853142581990496477">"దృగ్విన్యాసం"</string>
<string name="label_pages" msgid="7768589729282182230">"పేజీలు"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ప్రింటర్ ఎంచుకోండి"</string>
<string name="template_all_pages" msgid="3322235982020148762">"మొత్తం <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> పరిధి"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ఉదా. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"ముద్రణ సేవను ఎంచుకోండి"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ప్రింటర్ల కోసం శోధిస్తోంది"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ముద్రణ సేవలు ఏవీ ప్రారంభించలేదు"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ప్రింటర్లు కనుగొనబడలేదు"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను ముద్రిస్తోంది"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను రద్దు చేస్తోంది"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ప్రింటర్కు కనెక్షన్ లేదు"</string>
<string name="reason_unknown" msgid="5507940196503246139">"తెలియదు"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – అందుబాటులో లేదు"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ని ఉపయోగించాలా?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"మీ పత్రం ప్రింటర్కు వెళ్లే మార్గంలో ఒకటి లేదా అంతకంటే ఎక్కువ సర్వర్ల గుండా వెళ్లవచ్చు."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"నలుపు & తెలుపు"</item>
<item msgid="2762241247228983754">"రంగు"</item>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index 89e3082..c33a759 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"2 ด้าน"</string>
<string name="label_orientation" msgid="2853142581990496477">"การวางแนว"</string>
<string name="label_pages" msgid="7768589729282182230">"หน้า"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"เลือกเครื่องพิมพ์"</string>
<string name="template_all_pages" msgid="3322235982020148762">"ทั้ง <xliff:g id="PAGE_COUNT">%1$s</xliff:g> หน้า"</string>
<string name="template_page_range" msgid="428638530038286328">"ช่วง <xliff:g id="PAGE_COUNT">%1$s</xliff:g> หน้า"</string>
<string name="pages_range_example" msgid="8558694453556945172">"เช่น 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"เลือกบริการพิมพ์"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"กำลังค้นหาเครื่องพิมพ์"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"ไม่ได้เปิดใช้บริการพิมพ์"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ไม่พบเครื่องพิมพ์"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"กำลังพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"กำลังยกเลิก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ไม่ทราบ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"ใช้ <xliff:g id="SERVICE">%1$s</xliff:g> ไหม"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"เอกสารของคุณอาจต้องผ่านมากกว่าหนึ่งเซิร์ฟเวอร์ระหว่างส่งไปยังเครื่องพิมพ์"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ขาวดำ"</item>
<item msgid="2762241247228983754">"สี"</item>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 445d8b0..545bda4 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Two-sided"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oryentasyon"</string>
<string name="label_pages" msgid="7768589729282182230">"Mga Page"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Pumili ng printer"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Lahat ng <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Hanay ng <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"hal. 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Pumili ng serbisyo ng pag-print"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Naghahanap ng mga printer"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Walang mga naka-enable na serbisyo sa pag-print"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Walang mga printer na nakita"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Pini-print ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kinakansela ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"hindi alam"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – hindi available"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Gusto mo bang gamitin ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Bago ma-print ang iyong dokumento, maaari itong dumaan sa isa o higit pang mga server."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Kulay"</item>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 66d1e99..a13f2df 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Çift taraflı"</string>
<string name="label_orientation" msgid="2853142581990496477">"Sayfa yönü"</string>
<string name="label_pages" msgid="7768589729282182230">"Sayfa"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Yazıcı seçin"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> sayfanın tamamı"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> sayfalık aralık"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ör. 1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Yazdırma hizmetini seçin"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Yazıcılar aranıyor"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Etkin yazıcı hizmeti yok"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Yazıcı bulunamadı"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> yazdırılıyor"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> iptal ediliyor"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
<string name="reason_unknown" msgid="5507940196503246139">"bilinmiyor"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – kullanılamıyor"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> kullanılsın mı?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokümanınız yazıcıya giderken bir veya daha fazla sunucudan geçebilir."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Siyah Beyaz"</item>
<item msgid="2762241247228983754">"Renkli"</item>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index fcd3fa6..def21ab 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Двосторонній друк"</string>
<string name="label_orientation" msgid="2853142581990496477">"Орієнтація"</string>
<string name="label_pages" msgid="7768589729282182230">"Сторінки"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Виберіть принтер"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Усі <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Діапазон <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"напр.,1–5, 8, 11–13"</string>
@@ -62,6 +63,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Вибрати службу друку"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Пошук принтерів"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Немає служб друку"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтери не знайдено"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" друкується"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" скасовується"</string>
@@ -78,6 +80,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невідомо"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" не доступне"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Увімкнути службу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Коли ви надсилаєте документ на принтер, він може проходити через декілька серверів."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Чорно-білий"</item>
<item msgid="2762241247228983754">"Колір"</item>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 2c2c460..c031aba 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"دو طرف"</string>
<string name="label_orientation" msgid="2853142581990496477">"سمت بندی"</string>
<string name="label_pages" msgid="7768589729282182230">"صفحات"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ایک پرنٹر منتخب کریں"</string>
<string name="template_all_pages" msgid="3322235982020148762">"سبھی <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> کی رینج"</string>
<string name="pages_range_example" msgid="8558694453556945172">"مثلاً 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"پرنٹ سروس منتخب کریں"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"پرنٹرز تلاش کر رہا ہے"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"کوئی پرنٹ سروس فعال نہیں"</string>
<string name="print_no_printers" msgid="4869403323900054866">"کوئی پرنٹرز نہيں ملے"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> پرنٹ کررہا ہے"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو منسوخ کر رہا ہے"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"پرنٹر کے ساتھ کوئی کنکشن نہیں ہے"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – دستیاب نہیں ہے"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> استعمال کریں؟"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"آپ کی دستاویز پرنٹر تک جاتے ہوئے ممکن ہے ایک یا زیادہ سرورز سے گزرے۔"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"سیاہ و سفید"</item>
<item msgid="2762241247228983754">"رنگ"</item>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index f88eca5..59dcca9 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Ikki tomonlama"</string>
<string name="label_orientation" msgid="2853142581990496477">"Joylashuv"</string>
<string name="label_pages" msgid="7768589729282182230">"Sahifalar"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Printerni tanlang"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Barchasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="template_page_range" msgid="428638530038286328">"O‘zgarish chegarasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
<string name="pages_range_example" msgid="8558694453556945172">"masalan: 1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Chop etish xizmatini tanlang"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printerlar qidirilmoqda"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Hech qaysi chop etish xizmati yoqilmagan"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Printerlar topilmadi"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Chop etilmoqda: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bekor qilinmoqda"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printer ulanmagan"</string>
<string name="reason_unknown" msgid="5507940196503246139">"noma’lum"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – mavjud emas"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xizmatidan foydalanilsinmi?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Hujjatingiz chop etilishidan oldin bir yoki bir necha serverlardan o‘tishi mumkin."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Oq & qora"</item>
<item msgid="2762241247228983754">"Rang"</item>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index 2d1e8fa..0167823 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Hai mặt"</string>
<string name="label_orientation" msgid="2853142581990496477">"Hướng"</string>
<string name="label_pages" msgid="7768589729282182230">"Trang"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Chọn máy in"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Tất cả <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Phạm vi <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"Ví dụ: 1—5, 8, 11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Chọn dịch vụ in"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Đang tìm kiếm máy in"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Chưa kích hoạt dịch vụ in nào"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Không tìm thấy máy in"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"In <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hủy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
<string name="reason_unknown" msgid="5507940196503246139">"không xác định"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – không khả dụng"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Sử dụng <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Tài liệu của bạn có thể đi qua một hoặc nhiều máy chủ trên đường đến máy in."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Đen trắng"</item>
<item msgid="2762241247228983754">"Màu"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 553bc89..a74e994 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"双面"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="7768589729282182230">"页数"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"选择打印机"</string>
<string name="template_all_pages" msgid="3322235982020148762">"全部<xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>页"</string>
<string name="pages_range_example" msgid="8558694453556945172">"例如:1-5、8、11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"选择打印服务"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"未启用任何打印服务"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到打印机"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
<string name="reason_unknown" msgid="5507940196503246139">"未知"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - 无法使用"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文档可能会通过一个或多个服务器发送至打印机。"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 74b301f..35643f3 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"雙面"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="7768589729282182230">"頁數"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"選擇打印機"</string>
<string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
<string name="pages_range_example" msgid="8558694453556945172">"例如:1-5,8,11-13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋打印機"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"沒有已啟用的列印服務"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到打印機"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會通過一部或多部伺服器才傳送至打印機。"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index d91fe52..40c44ff 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"雙面"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="7768589729282182230">"頁面"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"選取印表機"</string>
<string name="template_all_pages" msgid="3322235982020148762">"全部 <xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> 頁"</string>
<string name="pages_range_example" msgid="8558694453556945172">"例如:1—5,8,11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋印表機"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"未啟用任何列印服務"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到印表機"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會透過一或多個伺服器輾轉傳送至印表機。"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 6ef2499..e0f6f34 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -27,6 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"Inezinhlangothi ezimbili"</string>
<string name="label_orientation" msgid="2853142581990496477">"Umumo"</string>
<string name="label_pages" msgid="7768589729282182230">"Amakhasi"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Khetha iphrinta"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Konke <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Ibanga le-<xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"isb. 1—5, 8, 11—13"</string>
@@ -60,6 +61,7 @@
</plurals>
<string name="choose_print_service" msgid="3740309762324459694">"Khetha isevisi yephrinta"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Isesha amaphrinta"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Amasevisi ephrinta akavuliwe."</string>
<string name="print_no_printers" msgid="4869403323900054866">"Awekho amaphrinta atholiwe"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Iphrinta i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ikhansela i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -74,6 +76,8 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"akwaziwa"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"I-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ayitholakali"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Sebenzisa i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Idokhumenti yakho ingase idlule iseva eyodwa noma amaningi lapho iya kuphrinta."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Okumnyama nokumhlophe"</item>
<item msgid="2762241247228983754">"Umbala"</item>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 50237832..6d81788 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -49,6 +49,9 @@
<!-- Label of the page selection widget. [CHAR LIMIT=20] -->
<string name="label_pages">Pages</string>
+ <!-- Label of the destination widget. [CHAR LIMIT=20] -->
+ <string name="destination_default_text">Select a printer</string>
+
<!-- Template for the all pages option in the page selection widget. [CHAR LIMIT=20] -->
<string name="template_all_pages">All <xliff:g id="page_count" example="100">%1$s</xliff:g></string>
@@ -149,6 +152,9 @@
<!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
<string name="print_searching_for_printers">Searching for printers</string>
+ <!-- Title for the prompt shown as a placeholder if there are no print services. [CHAR LIMIT=50] -->
+ <string name="print_no_print_services">No print services enabled</string>
+
<!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
<string name="print_no_printers">No printers found</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 33db831..f006ccb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -17,6 +17,7 @@
package com.android.printspooler.model;
import android.app.Notification;
+import android.app.Notification.Action;
import android.app.Notification.InboxStyle;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -24,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.PowerManager;
@@ -114,13 +116,25 @@
}
}
+ /**
+ * Create an {@link Action} that cancels a {@link PrintJobInfo print job}.
+ *
+ * @param printJob The {@link PrintJobInfo print job} to cancel
+ *
+ * @return An {@link Action} that will cancel a print job
+ */
+ private Action createCancelAction(PrintJobInfo printJob) {
+ return new Action.Builder(
+ Icon.createWithResource(mContext, R.drawable.stat_notify_cancelling),
+ mContext.getString(R.string.cancel), createCancelIntent(printJob)).build();
+ }
+
private void createPrintingNotification(PrintJobInfo printJob) {
Notification.Builder builder = new Notification.Builder(mContext)
.setContentIntent(createContentIntent(printJob.getId()))
.setSmallIcon(computeNotificationIcon(printJob))
.setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
+ .addAction(createCancelAction(printJob))
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
@@ -131,14 +145,16 @@
}
private void createFailedNotification(PrintJobInfo printJob) {
+ Action.Builder restartActionBuilder = new Action.Builder(
+ Icon.createWithResource(mContext, R.drawable.ic_restart),
+ mContext.getString(R.string.restart), createRestartIntent(printJob.getId()));
+
Notification.Builder builder = new Notification.Builder(mContext)
.setContentIntent(createContentIntent(printJob.getId()))
.setSmallIcon(computeNotificationIcon(printJob))
.setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
- .addAction(R.drawable.ic_restart, mContext.getString(R.string.restart),
- createRestartIntent(printJob.getId()))
+ .addAction(createCancelAction(printJob))
+ .addAction(restartActionBuilder.build())
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
@@ -153,8 +169,7 @@
.setContentIntent(createContentIntent(printJob.getId()))
.setSmallIcon(computeNotificationIcon(printJob))
.setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
+ .addAction(createCancelAction(printJob))
.setContentText(printJob.getPrinterName())
.setWhen(System.currentTimeMillis())
.setOngoing(true)
@@ -196,7 +211,7 @@
PrintJobInfo printJob = printJobs.get(i);
if (i == printJobCount - 1) {
builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
- computeNotificationIcon(printJob))).getBitmap());
+ computeNotificationIcon(printJob), null)).getBitmap());
builder.setSmallIcon(computeNotificationIcon(printJob));
builder.setContentTitle(computeNotificationTitle(printJob));
builder.setContentText(printJob.getPrinterName());
@@ -301,6 +316,7 @@
}
public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
+ @SuppressWarnings("hiding")
private static final String LOG_TAG = "NotificationBroadcastReceiver";
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 53e07e9..67e170f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -64,6 +64,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
@@ -125,6 +126,8 @@
private static final String FRAGMENT_TAG = "FRAGMENT_TAG";
+ private static final String HAS_PRINTED_PREF = "has_printed";
+
private static final int ORIENTATION_PORTRAIT = 0;
private static final int ORIENTATION_LANDSCAPE = 1;
@@ -187,6 +190,7 @@
private Spinner mDestinationSpinner;
private DestinationAdapter mDestinationSpinnerAdapter;
+ private boolean mShowDestinationPrompt;
private Spinner mMediaSizeSpinner;
private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
@@ -240,7 +244,9 @@
throw new IllegalArgumentException(PrintManager.EXTRA_PRINT_JOB
+ " cannot be null");
}
- mPrintJob.setAttributes(new PrintAttributes.Builder().build());
+ if (mPrintJob.getAttributes() == null) {
+ mPrintJob.setAttributes(new PrintAttributes.Builder().build());
+ }
final IBinder adapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
if (adapter == null) {
@@ -330,8 +336,8 @@
}
@Override
- public void onResume() {
- super.onResume();
+ public void onStart() {
+ super.onStart();
if (mState != STATE_INITIALIZING && mCurrentPrinter != null) {
mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
}
@@ -373,10 +379,15 @@
}
}
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
mPrinterAvailabilityDetector.cancel();
mPrinterRegistry.setTrackedPrinter(null);
- super.onPause();
+ super.onStop();
}
@Override
@@ -967,7 +978,7 @@
if (newFragment != null) {
transaction.add(R.id.embedded_content_container, newFragment, FRAGMENT_TAG);
}
- transaction.commit();
+ transaction.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();
}
@@ -1091,6 +1102,7 @@
updateOptionsUi();
addCurrentPrinterToHistory();
+ setUserPrinted();
PageRange[] selectedPages = computeSelectedPages();
if (!Arrays.equals(mSelectedPages, selectedPages)) {
@@ -1193,6 +1205,29 @@
// Print button
mPrintButton = (ImageView) findViewById(R.id.print_button);
mPrintButton.setOnClickListener(clickListener);
+
+ // Special prompt instead of destination spinner for the first time the user printed
+ if (!hasUserEverPrinted()) {
+ mShowDestinationPrompt = true;
+
+ mSummaryCopies.setEnabled(false);
+ mSummaryPaperSize.setEnabled(false);
+
+ mDestinationSpinner.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mShowDestinationPrompt = false;
+ mSummaryCopies.setEnabled(true);
+ mSummaryPaperSize.setEnabled(true);
+ updateOptionsUi();
+
+ mDestinationSpinner.setOnTouchListener(null);
+ mDestinationSpinnerAdapter.notifyDataSetChanged();
+
+ return false;
+ }
+ });
+ }
}
/**
@@ -1330,6 +1365,22 @@
&& printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
}
+ /**
+ * Disable all options UI elements, beside the {@link #mDestinationSpinner}
+ */
+ private void disableOptionsUi() {
+ mCopiesEditText.setEnabled(false);
+ mCopiesEditText.setFocusable(false);
+ mMediaSizeSpinner.setEnabled(false);
+ mColorModeSpinner.setEnabled(false);
+ mDuplexModeSpinner.setEnabled(false);
+ mOrientationSpinner.setEnabled(false);
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeEditText.setEnabled(false);
+ mPrintButton.setVisibility(View.GONE);
+ mMoreOptionsButton.setEnabled(false);
+ }
+
void updateOptionsUi() {
// Always update the summary.
updateSummary();
@@ -1344,32 +1395,14 @@
if (mState != STATE_PRINTER_UNAVAILABLE) {
mDestinationSpinner.setEnabled(false);
}
- mCopiesEditText.setEnabled(false);
- mCopiesEditText.setFocusable(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mDuplexModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setVisibility(View.GONE);
- mMoreOptionsButton.setEnabled(false);
+ disableOptionsUi();
return;
}
// If no current printer, or it has no capabilities, or it is not
// available, we disable all print options except the destination.
if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
- mCopiesEditText.setEnabled(false);
- mCopiesEditText.setFocusable(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mDuplexModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setVisibility(View.GONE);
- mMoreOptionsButton.setEnabled(false);
+ disableOptionsUi();
return;
}
@@ -1677,6 +1710,10 @@
mCopiesEditText.setText(MIN_COPIES_STRING);
mCopiesEditText.requestFocus();
}
+
+ if (mShowDestinationPrompt) {
+ disableOptionsUi();
+ }
}
private void updateSummary() {
@@ -1978,6 +2015,32 @@
}
}
+
+ /**
+ * Check if the user has ever printed a document
+ *
+ * @return true iff the user has ever printed a document
+ */
+ private boolean hasUserEverPrinted() {
+ SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE);
+
+ return preferences.getBoolean(HAS_PRINTED_PREF, false);
+ }
+
+ /**
+ * Remember that the user printed a document
+ */
+ private void setUserPrinted() {
+ SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE);
+
+ if (!preferences.getBoolean(HAS_PRINTED_PREF, false)) {
+ SharedPreferences.Editor edit = preferences.edit();
+
+ edit.putBoolean(HAS_PRINTED_PREF, true);
+ edit.apply();
+ }
+ }
+
private final class DestinationAdapter extends BaseAdapter
implements PrinterRegistry.OnPrintersChangeListener {
private final List<PrinterHolder> mPrinterHolders = new ArrayList<>();
@@ -1986,6 +2049,11 @@
private boolean mHistoricalPrintersLoaded;
+ /**
+ * Has the {@link #mDestinationSpinner} ever used a view from printer_dropdown_prompt
+ */
+ private boolean hadPromptView;
+
public DestinationAdapter() {
mHistoricalPrintersLoaded = mPrinterRegistry.areHistoricalPrintersLoaded();
if (mHistoricalPrintersLoaded) {
@@ -2096,9 +2164,20 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.printer_dropdown_item, parent, false);
+ if (mShowDestinationPrompt) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_dropdown_prompt, parent, false);
+ hadPromptView = true;
+ }
+
+ return convertView;
+ } else {
+ // We don't know if we got an recyled printer_dropdown_prompt, hence do not use it
+ if (hadPromptView || convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_dropdown_item, parent, false);
+ }
}
CharSequence title = null;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 3905bada..f4c15bd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -84,6 +84,11 @@
private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+ /**
+ * If there are any enabled print services
+ */
+ private boolean mHasEnabledPrintServices;
+
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
new ArrayList<>();
@@ -175,10 +180,6 @@
}
});
- if (mAddPrinterServices.isEmpty()) {
- menu.removeItem(R.id.action_add_printer);
- }
-
return true;
}
@@ -230,6 +231,7 @@
public void onResume() {
super.onResume();
updateServicesWithAddPrinterActivity();
+ updateEmptyView((DestinationAdapter)mListView.getAdapter());
invalidateOptionsMenu();
}
@@ -258,6 +260,7 @@
}
private void updateServicesWithAddPrinterActivity() {
+ mHasEnabledPrintServices = true;
mAddPrinterServices.clear();
// Get all enabled print services.
@@ -266,6 +269,7 @@
// No enabled print services - done.
if (enabledServices.isEmpty()) {
+ mHasEnabledPrintServices = false;
return;
}
@@ -324,7 +328,10 @@
}
TextView titleView = (TextView) findViewById(R.id.title);
View progressBar = findViewById(R.id.progress_bar);
- if (adapter.getUnfilteredCount() <= 0) {
+ if (!mHasEnabledPrintServices) {
+ titleView.setText(R.string.print_no_print_services);
+ progressBar.setVisibility(View.GONE);
+ } else if (adapter.getUnfilteredCount() <= 0) {
titleView.setText(R.string.print_searching_for_printers);
progressBar.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9e48849..a37196e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -348,4 +348,7 @@
<!-- Header for items under the work user [CHAR LIMIT=30] -->
<string name="category_work">Work</string>
+ <!-- Full package name of OEM preferred device feedback reporter. Leave this blank, overlaid in Settings/TvSettings [DO NOT TRANSLATE] -->
+ <string name="oem_preferred_feedback_reporter" translatable="false" />
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
new file mode 100644
index 0000000..ff1c866
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -0,0 +1,172 @@
+/*
+ * 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.settingslib;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DeviceInfoUtils {
+ private static final String TAG = "DeviceInfoUtils";
+
+ private static final String FILENAME_PROC_VERSION = "/proc/version";
+ private static final String FILENAME_MSV = "/sys/board_properties/soc/msv";
+
+ /**
+ * Reads a line from the specified file.
+ * @param filename the file to read from
+ * @return the first line, if any.
+ * @throws IOException if the file couldn't be read
+ */
+ private static String readLine(String filename) throws IOException {
+ BufferedReader reader = new BufferedReader(new FileReader(filename), 256);
+ try {
+ return reader.readLine();
+ } finally {
+ reader.close();
+ }
+ }
+
+ public static String getFormattedKernelVersion() {
+ try {
+ return formatKernelVersion(readLine(FILENAME_PROC_VERSION));
+ } catch (IOException e) {
+ Log.e(TAG, "IO Exception when getting kernel version for Device Info screen",
+ e);
+
+ return "Unavailable";
+ }
+ }
+
+ public static String formatKernelVersion(String rawKernelVersion) {
+ // Example (see tests for more):
+ // Linux version 3.0.31-g6fb96c9 (android-build@xxx.xxx.xxx.xxx.com) \
+ // (gcc version 4.6.x-xxx 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT \
+ // Thu Jun 28 11:02:39 PDT 2012
+
+ final String PROC_VERSION_REGEX =
+ "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
+ "\\((\\S+?)\\) " + /* group 2: "x@y.com" (kernel builder) */
+ "(?:\\(gcc.+? \\)) " + /* ignore: GCC version information */
+ "(#\\d+) " + /* group 3: "#1" */
+ "(?:.*?)?" + /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
+ "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
+
+ Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
+ if (!m.matches()) {
+ Log.e(TAG, "Regex did not match on /proc/version: " + rawKernelVersion);
+ return "Unavailable";
+ } else if (m.groupCount() < 4) {
+ Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount()
+ + " groups");
+ return "Unavailable";
+ }
+ return m.group(1) + "\n" + // 3.0.31-g6fb96c9
+ m.group(2) + " " + m.group(3) + "\n" + // x@y.com #1
+ m.group(4); // Thu Jun 28 11:02:39 PDT 2012
+ }
+
+ /**
+ * Returns " (ENGINEERING)" if the msv file has a zero value, else returns "".
+ * @return a string to append to the model number description.
+ */
+ public static String getMsvSuffix() {
+ // Production devices should have a non-zero value. If we can't read it, assume it's a
+ // production device so that we don't accidentally show that it's an ENGINEERING device.
+ try {
+ String msv = readLine(FILENAME_MSV);
+ // Parse as a hex number. If it evaluates to a zero, then it's an engineering build.
+ if (Long.parseLong(msv, 16) == 0) {
+ return " (ENGINEERING)";
+ }
+ } catch (IOException|NumberFormatException e) {
+ // Fail quietly, as the file may not exist on some devices, or may be unreadable
+ }
+ return "";
+ }
+
+ public static String getFeedbackReporterPackage(Context context) {
+ final String feedbackReporter =
+ context.getResources().getString(R.string.oem_preferred_feedback_reporter);
+ if (TextUtils.isEmpty(feedbackReporter)) {
+ // Reporter not configured. Return.
+ return feedbackReporter;
+ }
+ // Additional checks to ensure the reporter is on system image, and reporter is
+ // configured to listen to the intent. Otherwise, dont show the "send feedback" option.
+ final Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> resolvedPackages =
+ pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
+ for (ResolveInfo info : resolvedPackages) {
+ if (info.activityInfo != null) {
+ if (!TextUtils.isEmpty(info.activityInfo.packageName)) {
+ try {
+ ApplicationInfo ai =
+ pm.getApplicationInfo(info.activityInfo.packageName, 0);
+ if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // Package is on the system image
+ if (TextUtils.equals(
+ info.activityInfo.packageName, feedbackReporter)) {
+ return feedbackReporter;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // No need to do anything here.
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static String getSecurityPatch() {
+ String patch = Build.VERSION.SECURITY_PATCH;
+ if (!"".equals(patch)) {
+ try {
+ SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
+ Date patchDate = template.parse(patch);
+ String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy");
+ patch = DateFormat.format(format, patchDate).toString();
+ } catch (ParseException e) {
+ // broken parse; fall through and use the raw string
+ }
+ return patch;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/accounts/AuthenticatorHelper.java b/packages/SettingsLib/src/com/android/settingslib/accounts/AuthenticatorHelper.java
new file mode 100644
index 0000000..ef511bb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/accounts/AuthenticatorHelper.java
@@ -0,0 +1,269 @@
+/*
+ * 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.settingslib.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SyncAdapterType;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for monitoring accounts on the device for a given user.
+ *
+ * Classes using this helper should implement {@link OnAccountsUpdateListener}.
+ * {@link OnAccountsUpdateListener#onAccountsUpdate(UserHandle)} will then be
+ * called once accounts get updated. For setting up listening for account
+ * updates, {@link #listenToAccountUpdates()} and
+ * {@link #stopListeningToAccountUpdates()} should be used.
+ */
+final public class AuthenticatorHelper extends BroadcastReceiver {
+ private static final String TAG = "AuthenticatorHelper";
+
+ private final Map<String, AuthenticatorDescription> mTypeToAuthDescription = new HashMap<>();
+ private final ArrayList<String> mEnabledAccountTypes = new ArrayList<>();
+ private final Map<String, Drawable> mAccTypeIconCache = new HashMap<>();
+ private final HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = new HashMap<>();
+
+ private final UserHandle mUserHandle;
+ private final Context mContext;
+ private final OnAccountsUpdateListener mListener;
+ private boolean mListeningToAccountUpdates;
+
+ public interface OnAccountsUpdateListener {
+ void onAccountsUpdate(UserHandle userHandle);
+ }
+
+ public AuthenticatorHelper(Context context, UserHandle userHandle,
+ OnAccountsUpdateListener listener) {
+ mContext = context;
+ mUserHandle = userHandle;
+ mListener = listener;
+ // This guarantees that the helper is ready to use once constructed: the account types and
+ // authorities are initialized
+ onAccountsUpdated(null);
+ }
+
+ public String[] getEnabledAccountTypes() {
+ return mEnabledAccountTypes.toArray(new String[mEnabledAccountTypes.size()]);
+ }
+
+ public void preloadDrawableForType(final Context context, final String accountType) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ getDrawableForType(context, accountType);
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ /**
+ * Gets an icon associated with a particular account type. If none found, return null.
+ * @param accountType the type of account
+ * @return a drawable for the icon or a default icon returned by
+ * {@link PackageManager#getDefaultActivityIcon} if one cannot be found.
+ */
+ public Drawable getDrawableForType(Context context, final String accountType) {
+ Drawable icon = null;
+ synchronized (mAccTypeIconCache) {
+ if (mAccTypeIconCache.containsKey(accountType)) {
+ return mAccTypeIconCache.get(accountType);
+ }
+ }
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ try {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ Context authContext = context.createPackageContextAsUser(desc.packageName, 0,
+ mUserHandle);
+ icon = mContext.getPackageManager().getUserBadgedIcon(
+ authContext.getDrawable(desc.iconId), mUserHandle);
+ synchronized (mAccTypeIconCache) {
+ mAccTypeIconCache.put(accountType, icon);
+ }
+ } catch (PackageManager.NameNotFoundException|Resources.NotFoundException e) {
+ // Ignore
+ }
+ }
+ if (icon == null) {
+ icon = context.getPackageManager().getDefaultActivityIcon();
+ }
+ return icon;
+ }
+
+ /**
+ * Gets the label associated with a particular account type. If none found, return null.
+ * @param accountType the type of account
+ * @return a CharSequence for the label or null if one cannot be found.
+ */
+ public CharSequence getLabelForType(Context context, final String accountType) {
+ CharSequence label = null;
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ try {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ Context authContext = context.createPackageContextAsUser(desc.packageName, 0,
+ mUserHandle);
+ label = authContext.getResources().getText(desc.labelId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "No label name for account type " + accountType);
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "No label icon for account type " + accountType);
+ }
+ }
+ return label;
+ }
+
+ /**
+ * Gets the package associated with a particular account type. If none found, return null.
+ * @param accountType the type of account
+ * @return the package name or null if one cannot be found.
+ */
+ public String getPackageForType(final String accountType) {
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ return desc.packageName;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the resource id of the label associated with a particular account type. If none found,
+ * return -1.
+ * @param accountType the type of account
+ * @return a resource id for the label or -1 if none found;
+ */
+ public int getLabelIdForType(final String accountType) {
+ if (mTypeToAuthDescription.containsKey(accountType)) {
+ AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+ return desc.labelId;
+ }
+ return -1;
+ }
+
+ /**
+ * Updates provider icons. Subclasses should call this in onCreate()
+ * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
+ */
+ public void updateAuthDescriptions(Context context) {
+ AuthenticatorDescription[] authDescs = AccountManager.get(context)
+ .getAuthenticatorTypesAsUser(mUserHandle.getIdentifier());
+ for (int i = 0; i < authDescs.length; i++) {
+ mTypeToAuthDescription.put(authDescs[i].type, authDescs[i]);
+ }
+ }
+
+ public boolean containsAccountType(String accountType) {
+ return mTypeToAuthDescription.containsKey(accountType);
+ }
+
+ public AuthenticatorDescription getAccountTypeDescription(String accountType) {
+ return mTypeToAuthDescription.get(accountType);
+ }
+
+ public boolean hasAccountPreferences(final String accountType) {
+ if (containsAccountType(accountType)) {
+ AuthenticatorDescription desc = getAccountTypeDescription(accountType);
+ if (desc != null && desc.accountPreferencesId != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void onAccountsUpdated(Account[] accounts) {
+ updateAuthDescriptions(mContext);
+ if (accounts == null) {
+ accounts = AccountManager.get(mContext).getAccountsAsUser(mUserHandle.getIdentifier());
+ }
+ mEnabledAccountTypes.clear();
+ mAccTypeIconCache.clear();
+ for (int i = 0; i < accounts.length; i++) {
+ final Account account = accounts[i];
+ if (!mEnabledAccountTypes.contains(account.type)) {
+ mEnabledAccountTypes.add(account.type);
+ }
+ }
+ buildAccountTypeToAuthoritiesMap();
+ if (mListeningToAccountUpdates) {
+ mListener.onAccountsUpdate(mUserHandle);
+ }
+ }
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ // TODO: watch for package upgrades to invalidate cache; see http://b/7206643
+ final Account[] accounts = AccountManager.get(mContext)
+ .getAccountsAsUser(mUserHandle.getIdentifier());
+ onAccountsUpdated(accounts);
+ }
+
+ public void listenToAccountUpdates() {
+ if (!mListeningToAccountUpdates) {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
+ // At disk full, certain actions are blocked (such as writing the accounts to storage).
+ // It is useful to also listen for recovery from disk full to avoid bugs.
+ intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+ mContext.registerReceiverAsUser(this, mUserHandle, intentFilter, null, null);
+ mListeningToAccountUpdates = true;
+ }
+ }
+
+ public void stopListeningToAccountUpdates() {
+ if (mListeningToAccountUpdates) {
+ mContext.unregisterReceiver(this);
+ mListeningToAccountUpdates = false;
+ }
+ }
+
+ public ArrayList<String> getAuthoritiesForAccountType(String type) {
+ return mAccountTypeToAuthorities.get(type);
+ }
+
+ private void buildAccountTypeToAuthoritiesMap() {
+ mAccountTypeToAuthorities.clear();
+ SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
+ mUserHandle.getIdentifier());
+ for (int i = 0, n = syncAdapters.length; i < n; i++) {
+ final SyncAdapterType sa = syncAdapters[i];
+ ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
+ if (authorities == null) {
+ authorities = new ArrayList<String>();
+ mAccountTypeToAuthorities.put(sa.accountType, authorities);
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Added authority " + sa.authority + " to accountType "
+ + sa.accountType);
+ }
+ authorities.add(sa.authority);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e4b1ed8..d994841 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -808,7 +808,12 @@
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) {
- setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ if (mDevice.getBluetoothClass().getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
+ setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ } else {
+ setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index f5fc698..6102bef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -18,6 +18,7 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
import android.app.Activity;
+import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;
import android.util.Pair;
@@ -54,6 +55,13 @@
return;
}
Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
+ if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
+ toolbar.setVisibility(View.GONE);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ mDrawerLayout = null;
+ return;
+ }
setActionBar(toolbar);
mDrawerAdapter = new SettingsDrawerAdapter(this);
ListView listView = (ListView) findViewById(R.id.left_drawer);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java b/packages/SettingsLib/src/com/android/settingslib/net/MobileDataController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
rename to packages/SettingsLib/src/com/android/settingslib/net/MobileDataController.java
index a7fdadc..642b60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/MobileDataController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
-
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.telephony.TelephonyManager.SIM_STATE_READY;
-import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
-import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+package com.android.settingslib.net;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -42,7 +35,14 @@
import java.util.Date;
import java.util.Locale;
-public class MobileDataControllerImpl implements NetworkController.MobileDataController {
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+
+public class MobileDataController {
private static final String TAG = "MobileDataController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -60,9 +60,9 @@
private INetworkStatsSession mSession;
private Callback mCallback;
- private NetworkControllerImpl mNetworkController;
+ private NetworkNameProvider mNetworkController;
- public MobileDataControllerImpl(Context context) {
+ public MobileDataController(Context context) {
mContext = context;
mTelephonyManager = TelephonyManager.from(context);
mConnectivityManager = ConnectivityManager.from(context);
@@ -71,7 +71,7 @@
mPolicyManager = NetworkPolicyManager.from(mContext);
}
- public void setNetworkController(NetworkControllerImpl networkController) {
+ public void setNetworkController(NetworkNameProvider networkController) {
mNetworkController = networkController;
}
@@ -161,7 +161,7 @@
} else {
usage.warningLevel = DEFAULT_WARNING_LEVEL;
}
- if (usage != null) {
+ if (usage != null && mNetworkController != null) {
usage.carrier = mNetworkController.getMobileDataNetworkName();
}
return usage;
@@ -231,6 +231,18 @@
}
}
+ public interface NetworkNameProvider {
+ String getMobileDataNetworkName();
+ }
+
+ public static class DataUsageInfo {
+ public String carrier;
+ public String period;
+ public long limitLevel;
+ public long warningLevel;
+ public long usageLevel;
+ }
+
public interface Callback {
void onMobileDataEnabled(boolean enabled);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
new file mode 100644
index 0000000..f5e39be
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
@@ -0,0 +1,100 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.Context;
+import android.graphics.drawable.AnimatedRotateDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+public class AnimatedImageView extends ImageView {
+ private AnimatedRotateDrawable mDrawable;
+ private boolean mAnimating;
+
+ public AnimatedImageView(Context context) {
+ super(context);
+ }
+
+ public AnimatedImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private void updateDrawable() {
+ if (isShown() && mDrawable != null) {
+ mDrawable.stop();
+ }
+ final Drawable drawable = getDrawable();
+ if (drawable instanceof AnimatedRotateDrawable) {
+ mDrawable = (AnimatedRotateDrawable) drawable;
+ // TODO: define in drawable xml once we have public attrs.
+ mDrawable.setFramesCount(56);
+ mDrawable.setFramesDuration(32);
+ if (isShown() && mAnimating) {
+ mDrawable.start();
+ }
+ } else {
+ mDrawable = null;
+ }
+ }
+
+ private void updateAnimating() {
+ if (mDrawable != null) {
+ if (isShown() && mAnimating) {
+ mDrawable.start();
+ } else {
+ mDrawable.stop();
+ }
+ }
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ super.setImageDrawable(drawable);
+ updateDrawable();
+ }
+
+ @Override
+ public void setImageResource(int resid) {
+ super.setImageResource(resid);
+ updateDrawable();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updateAnimating();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ updateAnimating();
+ }
+
+ public void setAnimating(boolean animating) {
+ mAnimating = animating;
+ updateAnimating();
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int vis) {
+ super.onVisibilityChanged(changedView, vis);
+ updateAnimating();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
new file mode 100644
index 0000000..fabae57
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -0,0 +1,79 @@
+/*
+ * 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.settingslib.wifi;
+
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+
+import java.util.List;
+
+public class WifiStatusTracker {
+
+ private final WifiManager mWifiManager;
+ public boolean enabled;
+ public boolean connected;
+ public String ssid;
+ public int rssi;
+ public int level;
+
+ public WifiStatusTracker(WifiManager wifiManager) {
+ mWifiManager = wifiManager;
+ }
+
+ public void handleBroadcast(Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ final NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ connected = networkInfo != null && networkInfo.isConnected();
+ // If Connected grab the signal strength and ssid.
+ if (connected) {
+ // try getting it out of the intent first
+ WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
+ ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
+ : mWifiManager.getConnectionInfo();
+ if (info != null) {
+ ssid = getSsid(info);
+ } else {
+ ssid = null;
+ }
+ } else if (!connected) {
+ ssid = null;
+ }
+ } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ // Default to -200 as its below WifiManager.MIN_RSSI.
+ rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+ level = WifiManager.calculateSignalLevel(rssi, 5);
+ }
+ }
+
+ private String getSsid(WifiInfo info) {
+ String ssid = info.getSSID();
+ if (ssid != null) {
+ return ssid;
+ }
+ // OK, it's not in the connectionInfo; we have to go hunting for it
+ List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
+ int length = networks.size();
+ for (int i = 0; i < length; i++) {
+ if (networks.get(i).networkId == info.getNetworkId()) {
+ return networks.get(i).SSID;
+ }
+ }
+ return null;
+ }
+}
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 71aefad..ba991fb 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,11 +8,12 @@
android:process="system"
android:backupAgent="SettingsBackupAgent"
android:killAfterRestore="false"
- android:icon="@mipmap/ic_launcher_settings">
-
- <!-- todo add: android:neverEncrypt="true" -->
+ android:icon="@mipmap/ic_launcher_settings"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
- <provider android:name="SettingsProvider" android:authorities="settings"
+ <provider android:name="SettingsProvider"
+ android:authorities="settings"
android:multiprocess="false"
android:exported="true"
android:singleUser="true"
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index b9a9c24..2e96f18 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -159,6 +159,7 @@
String key_mgmt = "";
boolean certUsed = false;
boolean hasWepKey = false;
+ boolean isEap = false;
final ArrayList<String> rawLines = new ArrayList<String>();
public static Network readFromStream(BufferedReader in) {
@@ -189,6 +190,9 @@
ssid = line;
} else if (line.startsWith("key_mgmt=")) {
key_mgmt = line;
+ if (line.contains("EAP")) {
+ isEap = true;
+ }
} else if (line.startsWith("client_cert=")) {
certUsed = true;
} else if (line.startsWith("ca_cert=")) {
@@ -197,6 +201,8 @@
certUsed = true;
} else if (line.startsWith("wep_")) {
hasWepKey = true;
+ } else if (line.startsWith("eap=")) {
+ isEap = true;
}
}
@@ -325,6 +331,13 @@
continue;
}
}
+ // Don't propagate EAP network definitions
+ if (net.isEap) {
+ if (DEBUG_BACKUP) {
+ Log.v(TAG, "Skipping EAP network " + net.ssid + " / " + net.key_mgmt);
+ }
+ continue;
+ }
if (! mKnownNetworks.contains(net)) {
if (DEBUG_BACKUP) {
Log.v(TAG, "Adding " + net.ssid + " / " + net.key_mgmt);
@@ -353,6 +366,12 @@
continue;
}
+ if (net.isEap) {
+ // Similarly, omit EAP network definitions to avoid propagating
+ // controlled enterprise network definitions.
+ continue;
+ }
+
net.write(w);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 8b1caf9..fbf8a2b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -54,7 +54,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -70,7 +69,6 @@
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -148,22 +146,6 @@
private static final Bundle NULL_SETTING = Bundle.forPair(Settings.NameValueTable.VALUE, null);
- // Per user settings that cannot be modified if associated user restrictions are enabled.
- private static final Map<String, String> sSettingToUserRestrictionMap = new ArrayMap<>();
- static {
- sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_MODE,
- UserManager.DISALLOW_SHARE_LOCATION);
- sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- UserManager.DISALLOW_SHARE_LOCATION);
- sSettingToUserRestrictionMap.put(Settings.Secure.INSTALL_NON_MARKET_APPS,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- sSettingToUserRestrictionMap.put(Settings.Global.ADB_ENABLED,
- UserManager.DISALLOW_DEBUGGING_FEATURES);
- sSettingToUserRestrictionMap.put(Settings.Global.PACKAGE_VERIFIER_ENABLE,
- UserManager.ENSURE_VERIFY_APPS);
- sSettingToUserRestrictionMap.put(Settings.Global.PREFERRED_NETWORK_MODE,
- UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- }
// Per user secure settings that moved to the for all users global settings.
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
@@ -647,8 +629,9 @@
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
- // If this is a setting that is currently restricted for this user, done.
- if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) {
+ // If this is a setting that is currently restricted for this user, do not allow
+ // unrestricting changes.
+ if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {
return false;
}
@@ -772,8 +755,9 @@
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
- // If this is a setting that is currently restricted for this user, done.
- if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) {
+ // If this is a setting that is currently restricted for this user, do not allow
+ // unrestricting changes.
+ if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {
return false;
}
@@ -904,12 +888,12 @@
}
}
- // Enforce what the calling package can mutate the system settings.
- enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, runAsUserId);
-
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId);
+ // Enforce what the calling package can mutate the system settings.
+ enforceRestrictedSystemSettingsMutationForCallingPackage(operation, name, callingUserId);
+
// Determine the owning user as some profile settings are cloned from the parent.
final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
@@ -978,12 +962,59 @@
return false;
}
- private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId) {
- String restriction = sSettingToUserRestrictionMap.get(setting);
- if (restriction == null) {
- return false;
+ /**
+ * Checks whether changing a setting to a value is prohibited by the corresponding user
+ * restriction.
+ *
+ * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestrictionLR},
+ * which should be in sync with this method.
+ *
+ * @return true if the change is prohibited, false if the change is allowed.
+ */
+ private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId,
+ String value) {
+ String restriction;
+ switch (setting) {
+ case Settings.Secure.LOCATION_MODE:
+ // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED
+ // in android.provider.Settings.Secure.putStringForUser(), so we shouldn't come
+ // here normally, but we still protect it here from a direct provider write.
+ if (String.valueOf(Settings.Secure.LOCATION_MODE_OFF).equals(value)) return false;
+ restriction = UserManager.DISALLOW_SHARE_LOCATION;
+ break;
+
+ case Settings.Secure.LOCATION_PROVIDERS_ALLOWED:
+ // See SettingsProvider.updateLocationProvidersAllowedLocked. "-" is to disable
+ // a provider, which should be allowed even if the user restriction is set.
+ if (value != null && value.startsWith("-")) return false;
+ restriction = UserManager.DISALLOW_SHARE_LOCATION;
+ break;
+
+ case Settings.Secure.INSTALL_NON_MARKET_APPS:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ break;
+
+ case Settings.Global.ADB_ENABLED:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_DEBUGGING_FEATURES;
+ break;
+
+ case Settings.Global.PACKAGE_VERIFIER_ENABLE:
+ case Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB:
+ if ("1".equals(value)) return false;
+ restriction = UserManager.ENSURE_VERIFY_APPS;
+ break;
+
+ case Settings.Global.PREFERRED_NETWORK_MODE:
+ restriction = UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
+ break;
+
+ default:
+ return false;
}
- return mUserManager.hasUserRestriction(restriction, new UserHandle(userId));
+
+ return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
}
private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) {
@@ -1094,6 +1125,9 @@
* But helper functions in android.providers.Settings can enable or disable
* a single provider by using a "+" or "-" prefix before the provider name.
*
+ * <p>See also {@link #isGlobalOrSecureSettingRestrictedForUser()}. If DISALLOW_SHARE_LOCATION
+ * is set, the said method will only allow values with the "-" prefix.
+ *
* @returns whether the enabled location providers changed.
*/
private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 05591cc..8c39ee6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -108,7 +108,9 @@
<uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
- <application android:label="@string/app_label">
+ <application android:label="@string/app_label"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.android.shell"
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index 43beb57..d1998bd 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutverslae bevat data van die stelsel se verskillende loglêers af, insluitend persoonlike en private inligting. Deel foutverslae net met programme en mense wat jy vertrou."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Wys hierdie boodskap volgende keer"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Foutverslae"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Foutverslaglêer kon nie gelees word nie"</string>
</resources>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index e86ecf8..6f905a9 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"የሳንካ ሪፖርቶች የግል መረጃን ጨምሮ ከበርካታ የስርዓቱ ምዝግብ ማስታወሻዎች የመጣ ውሂብን ይዟል። የሳንካ ሪፖርቶች ለሚያምኗቸው መተግበሪያዎችን እና ሰዎችን ብቻ ያጋሩ።"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ይህን መልዕክት በሚቀጥለው ጊዜ አሳይ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"የሳንካ ሪፖርቶች"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"የሳንካ ሪፖርት ፋይል ሊነበብ አልተቻለም"</string>
</resources>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index 0fcf019..76100b5 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"تحتوي تقارير الأخطاء على بيانات من ملفات سجلات النظام المتنوعة، بما في ذلك معلومات شخصية وخاصة. لا تشارك تقارير الأخطاء إلا مع التطبيقات والأشخاص الموثوق بهم."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"إظهار هذه الرسالة في المرة القادمة"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"تقارير الأخطاء"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"تعذرت قراءة ملف تقرير الخطأ."</string>
</resources>
diff --git a/packages/Shell/res/values-az-rAZ/strings.xml b/packages/Shell/res/values-az-rAZ/strings.xml
index e235eb8..d8176f5 100644
--- a/packages/Shell/res/values-az-rAZ/strings.xml
+++ b/packages/Shell/res/values-az-rAZ/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Baq raportları sistemin müxtəlif jurnal fayllarından data içərir ki, buna şəxsi və konfidensial məlumatlar da aiddir. Yalnız inandığınız adamlarla baq raportlarını paylaşın."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bu mesajı növbəti dəfə göstər"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Baq hesabatları"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Baq hesabat faylı oxunmur"</string>
</resources>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index 381d5d8..fc2dad0 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчетите за програмни грешки съдържат данни от различни регистрационни файлове на системата, включително лична и поверителна информация. Споделяйте ги само с приложения и хора, на които имате доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Това съобщение да се показва следващия път"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Отчети за прогр. грешки"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Файлът със сигнал за програмна грешка не можа да бъде прочетен"</string>
</resources>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index 76da84b..5aa3e9d 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"ত্রুটির প্রতিবেদনগুলিতে থাকা ডেটা, সিস্টেমের বিভিন্ন লগ ফাইলগুলি থেকে আসে, যাতে ব্যক্তিগত এবং গোপনীয় তথ্য অন্তর্ভুক্ত থাকে৷ আপনি বিশ্বাস করেন শুধুমাত্র এমন অ্যাপ্লিকেশান এবং ব্যক্তিদের সাথে ত্রুটির প্রতিবেদনগুলি ভাগ করুন৷"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"এই বার্তাটি পরের বার দেখান"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ত্রুটির প্রতিবেদনগুলি"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ত্রুটির প্রতিবেদনের ফাইলটি পড়া যায়নি"</string>
</resources>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index b07bafd..bef1a67 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Els informes d\'error contenen dades dels diferents fitxers de registre del sistema, inclosa informació privada i personal. Comparteix els informes d\'error només amb les aplicacions i amb les persones en qui confies."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra aquest missatge la propera vegada"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'error"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"No s\'ha pogut llegir el fitxer de l\'informe d\'errors"</string>
</resources>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index 3e36c6f..46c8f4e 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Chybová hlášení obsahují data z různých souborů protokolů systému včetně osobních a soukromých informací. Chybová hlášení sdílejte pouze s aplikacemi a uživateli, kterým důvěřujete."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobrazit tuto zprávu příště"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Zprávy o chybách"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Soubor chybové zprávy nelze načíst"</string>
</resources>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 8925b85..7a87b9b 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Fejlrapporter indeholder data fra systemets forskellige logfiler, f.eks. personlige og private oplysninger. Del kun fejlrapporter med apps og personer, du har tillid til."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne underretning næste gang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Fejlrapporter"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Fejlrapportfilen kunne ikke læses"</string>
</resources>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index 19d58fe..7a25c4d 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Fehlerberichte"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Fehlerberichtdatei konnte nicht gelesen werden."</string>
</resources>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 5fadaa4..02e37f7 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Αναφορές σφαλμάτων"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Δεν ήταν δυνατή η ανάγνωση του αρχείου της αναφοράς σφαλμάτων"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index b50fc2a..1b55115 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Bug report file could not be read"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index b50fc2a..1b55115 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Bug report file could not be read"</string>
</resources>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index b50fc2a..1b55115 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Bug report file could not be read"</string>
</resources>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index 06edfdc..1937349 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida la información personal y privada. Comparte los informes de errores únicamente con aplicaciones y personas en las que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de errores"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"No se pudo leer el archivo de informe de errores"</string>
</resources>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index 3398ca3..002bea9 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de error"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"No se ha podido leer el archivo del informe de errores"</string>
</resources>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index 549ee26..a16875c 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Veaaruanded sisaldavad andmeid erinevatest süsteemi logifailidest, sh isiklikku ja privaatset teavet. Jagage veaaruandeid ainult usaldusväärsete rakenduste ja inimestega."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Kuva see sõnum järgmisel korral"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Veaaruanded"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Veaaruande faili ei õnnestunud lugeda"</string>
</resources>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index 048ab8e..e7f1766 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Akatsen txostenek sistemaren erregistro-fitxategietako datuak dauzkate, informazio pertsonala eta pribatua barne. Akatsen txostenak partekatzen badituzu, partekatu soilik aplikazio eta pertsona fidagarriekin."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Erakutsi mezu hau hurrengoan"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ezin izan da irakurri akatsen txostena"</string>
</resources>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index f42ba81..9138b28 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"گزارشهای اشکال حاوی دادههایی از فایلهای گزارش مختلف در سیستم هستند، شامل اطلاعات شخصی و خصوصی. گزارشهای اشکال را فقط با افراد و برنامههای مورد اعتماد خود به اشتراک بگذارید."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"دفعه بعد این پیام نشان داده شود"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"گزارش اشکال"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"فایل گزارش اشکال خوانده نشد"</string>
</resources>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 26003b3..079433d 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Virheraportit sisältävät järjestelmän lokitietoja, ja niihin voi sisältyä henkilökohtaisia ja yksityisiä tietoja. Jaa virheraportteja vain luotettaville sovelluksille ja käyttäjille."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Näytä tämä viesti seuraavalla kerralla"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Virheraportit"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Virheraporttitiedostoa ei voi lukea."</string>
</resources>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index b4b81ec..4ead085 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bogue contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bogue qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports de bogues"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Impossible de lire le fichier du rapport de bogue"</string>
</resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index e801054..14c1d3b 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports d\'erreur"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Impossible de lire le fichier de rapport de bug."</string>
</resources>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index 5690c9e..61b0335 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Os informes de erros conteñen datos dos distintos ficheiros de rexistro do sistema, incluída información persoal e privada. Comparte os informes de erros unicamente con aplicacións e persoas de confianza."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensaxe a próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de erros"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Non se puido ler o ficheiro de informe de erros"</string>
</resources>
diff --git a/packages/Shell/res/values-gu-rIN/strings.xml b/packages/Shell/res/values-gu-rIN/strings.xml
index e9fdfdb..746ac45 100644
--- a/packages/Shell/res/values-gu-rIN/strings.xml
+++ b/packages/Shell/res/values-gu-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"બગ રિપોર્ટ્સ વ્યક્તિગત અને ખાનગી માહિતી સહિત, સિસ્ટમની વિભિન્ન લૉગ ફાઇલોનો ડેટા ધરાવે છે. બગ રિપોર્ટ્સ ફક્ત તમે વિશ્વાસ કરતા હો તે એપ્લિકેશનો અને લોકો સાથે જ શેર કરો."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"આગલી વખતે આ સંદેશ બતાવો"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"બગ રિપોર્ટ્સ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"બગ રીપોર્ટ ફાઇલ વાંચી શકાઇ નથી"</string>
</resources>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index aee1121..97db607 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्ट में व्यक्तिगत और निजी जानकारी सहित, सिस्टम की विभिन्न लॉग फ़ाइलों का डेटा होता है. बग रिपोर्ट केवल विश्वसनीय ऐप्स और व्यक्तियों से ही साझा करें."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यह संदेश अगली बार दिखाएं"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्ट"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रिपोर्ट फ़ाइल नहीं पढ़ी जा सकी"</string>
</resources>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index a2cb3b0..b1fad84 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Prijave programskih pogrešaka sadržavaju podatke iz različitih datoteka zapisnika sustava, uključujući osobne i privatne informacije. Prijave programskih pogrešaka dijelite samo s aplikacijama i osobama koje smatrate pouzdanima."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži tu poruku sljedeći put"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Izvj. o prog. pogreš."</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Izvješće o programskoj pogrešci nije pročitano"</string>
</resources>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index f0bc227..7dae6c1 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"A programhiba-jelentések a rendszer különféle naplófájljaiból származó adatokat tartalmaznak, köztük személyes és magánjellegű információkat is. Csak olyan alkalmazásokkal és személyekkel osszon meg programhiba-jelentéseket, amelyekben vagy akikben megbízik."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Üzenet mutatása legközelebb"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hibajelentések"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"A hibajelentési fájlt nem sikerült beolvasni"</string>
</resources>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index 6a5358b..149b677 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մուտքի ֆայլերից, այդ թվում նաև անհատական և գաղտնի տեղեկություններ: Վրիպակի զեկույցները կիսեք միայն այն հավելվածների և մարդկանց հետ, որոնց վստահում եք:"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Այս հաղորդագրությունը ցույց տալ հաջորդ անգամ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Հնարավոր չէ կարդալ վրիպակների զեկույցի ֆայլը"</string>
</resources>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index 627fc5e..534badc 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan bug berisi data dari berbagai file log sistem, termasuk informasi pribadi dan rahasia. Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tampilkan pesan ini lain kali"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"File laporan bug tidak dapat dibaca"</string>
</resources>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index dbd39c4..ce742f6 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Villutilkynningar innihalda gögn úr hinum ýmsu annálsskrám kerfisins, þ. á m. persónuleg gögn og trúnaðarupplýsingar. Deildu villutilkynningum eingöngu með forritum og fólki sem þú treystir."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Sýna þessi skilaboð næst"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Villutilkynningar"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ekki var hægt að lesa úr villuskýrslunni"</string>
</resources>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index cd63891..38e360f 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Le segnalazioni di bug contengono dati da vari file di log del sistema, incluse informazioni personali e private. Condividi le segnalazioni di bug solo con app e persone attendibili."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra questo messaggio la prossima volta"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapporti sui bug"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Impossibile leggere il file relativo alla segnalazione di bug"</string>
</resources>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index 39c784f..ab9aecf 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, כולל מידע אישי ופרטי. שתף דוחות באגים רק עם אפליקציות ואנשים שאתה סומך עליהם."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"הצג את ההודעה הזו בפעם הבאה"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"דוחות באגים"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"לא ניתן היה לקרוא את קובץ הדוח על הבאג"</string>
</resources>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 48be802..619ee4f 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"バグレポート"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"バグレポート ファイルを読み取ることができませんでした"</string>
</resources>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index bb539d0..f98045f 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"ხარვეზის ანგარიშები მოიცავს მონაცემებს სხვადასხვა სისტემური ჟურნალის ფაილებიდან, მათ შორის პირად და კონფიდენციალურ ინფორმაციას."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"შემდგომში აჩვენე ეს შეტყობინება"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"შეცდომების ანგარიშები"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ხარვეზების შესახებ ანგარიშის წაკითხვა ვერ მოხერხდა"</string>
</resources>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index b22ca45..a24f2cc 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Вирус туралы баянатта жүйеде тіркелген әртүрлі файлдар туралы деректер болады, оған жеке және құпия ақпарат та кіреді. Вирус баянаттарын сенімді қолданбалар және сенімді адамдармен ғана бөлісіңіз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бұл хабарды келесі жолы көрсетіңіз"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Қате туралы баяндамалар"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Қате туралы есеп файлын оқу мүмкін болмады"</string>
</resources>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index 50c893b..2439248 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"របាយការណ៍កំហុសរួមមានឯកសារកំណត់ហេតុផ្សេងៗរបស់ប្រព័ន្ធ រួមមានព័ត៌មានផ្ទាល់ខ្លួន និងឯកជន។ ចែករំលែករបាយការណ៍កំហុសជាមួយកម្មវិធី និងមនុស្សដែលអ្នកទុកចិត្ត។"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"បង្ហាញសារនេះពេលក្រោយ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"រាយការណ៍ពីកំហុស"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"មិនអាចអានឯកសាររបាយកាណ៍កំហុសបានទេ"</string>
</resources>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index 7ab6abf..bca9c45 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"ವೈಯಕ್ತಿಕ ಮತ್ತು ಖಾಸಗಿ ಮಾಹಿತಿಯು ಸೇರಿದಂತೆ, ಸಿಸ್ಟಂನ ಹಲವಾರು ಲಾಗ್ ಫೈಲ್ಗಳಿಂದ ಡೇಟಾವನ್ನು ದೋಷದ ವರದಿಗಳು ಒಳಗೊಂಡಿವೆ. ನೀವು ನಂಬುವಂತಹ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಜನರೊಂದಿಗೆ ಮಾತ್ರ ದೋಷದ ವರದಿಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ಈ ಸಂದೇಶವನ್ನು ಮುಂದಿನ ಬಾರಿ ತೋರಿಸಿ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ದೋಷ ವರದಿಗಳು"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ಬಗ್ ವರದಿ ಫೈಲ್ ಅನ್ನು ಓದಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
</resources>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index da0b31f..a3a0bf3 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"버그 신고서는 시스템의 다양한 로그 파일 데이터(예: 개인 및 비공개 정보)를 포함합니다. 신뢰할 수 있는 앱과 사용자에게만 버그 신고서를 공유하세요."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"버그 신고"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"버그 신고 파일을 읽을 수 없습니다."</string>
</resources>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 7183f77..3fba743 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Ката тууралуу билдирүүлөр системанын ар кандай лог файлдарынын берилиштерин камтыйт, аларга өздүк жана купуя маалыматтар дагы кирет. Ката тууралуу билдирүүлөрдү сиз ишенген колдонмолор жана адамдар менен гана бөлүшүңүз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бул билдирүү кийин көрсөтүлсүн"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Мүчүлүштүктөрдү кабарлоолор"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Мүчүлүштүк тууралуу кабар берүү файлы окулбай койду"</string>
</resources>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index fcc58e9..537f197 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"ການລາຍງານຂໍ້ຜິດພາດປະກອບມີ ຂໍ້ມູນຈາກໄຟລ໌ບັນທຶກຂອງລະບົບຫຼາຍໄຟລ໌, ຮວມທັງຂໍ້ມູນສ່ວນໂຕນຳ. ທ່ານຕ້ອງແບ່ງປັນລາຍງານຂໍ້ຜິດພາດໃຫ້ແອັບຯ ແລະຄົນທີ່ທ່ານເຊື່ອຖືໄດ້ເທົ່ານັ້ນ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ສະແດງຂໍ້ຄວາມນີ້ອີກໃນເທື່ອຕໍ່ໄປ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ລາຍງານບັນຫາ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ບໍ່ສາມາດອ່ານໄຟລ໌ລາຍງານຂໍ້ຜິດພາດໄດ້"</string>
</resources>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index 51655a4..6965e4c 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Riktų ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Riktų ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Riktų ataskaitos"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Nepavyko sukurti pranešimo apie riktą failo"</string>
</resources>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index cf1a75a..efc40f3 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Kļūdu pārskatā ir iekļauti dati no dažādiem sistēmas žurnālfailiem, tostarp personas dati un privāta informācija. Kļūdu pārskatus ieteicams kopīgot tikai ar uzticamām lietotnēm un lietotājiem."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rādīt šo ziņojumu nākamajā reizē"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Kļūdu ziņojumi"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Nevarēja nolasīt kļūdas pārskata failu."</string>
</resources>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index 785a841..7571c2c 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаите за грешка содржат податоци од разни датотеки за евиденција на системот, вклучувајќи лични и приватни информации. Извештаите за грешка споделувајте ги само со апликации и луѓе на коишто им верувате."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ја поракава следниот пат"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаи за грешки"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Датотеката со извештај за грешка не можеше да се прочита"</string>
</resources>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index 8fa6d67c..4779e4d 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"വ്യക്തിഗതവും സ്വകാര്യവുമായ വിവരങ്ങൾ ഉൾപ്പെടെ, സിസ്റ്റത്തിന്റെ നിരവധി ലോഗ് ഫയലുകളിൽ നിന്നുള്ള ഡാറ്റ, ബഗ് റിപ്പോർട്ടുകളിൽ അടങ്ങിയിരിക്കുന്നു. നിങ്ങൾ വിശ്വസിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കും ആളുകൾക്കും മാത്രം ബഗ് റിപ്പോർട്ടുകൾ പങ്കിടുക."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ഈ സന്ദേശം അടുത്ത തവണ ദൃശ്യമാക്കുക"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ബഗ് റിപ്പോർട്ടുകൾ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ബഗ് റിപ്പോർട്ട് ഫയൽ വായിക്കാനായില്ല"</string>
</resources>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 4010072..71ad7f6 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Алдааны репорт нь хувийн болон нууц мэдээлэл зэргийг агуулсан системийн төрөл бүрийн лог файлын датаг агуулна. Алдааны репортыг зөвхөн итгэлтэй апп болон хүмүүст хуваалцана уу."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Энэ мессежийг дараагийн удаа харуулах"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Гэмтлийн тухай тайлан"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Алдааны тайлангийн файлыг уншиж чадахгүй байна"</string>
</resources>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index c4993fc..c32bb5b 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"दोष अहवालांमध्ये वैयक्तिक आणि खाजगी माहितीसह, सिस्टमच्या अनेक लॉग फायलींमधील डेटा असतो. केवळ आपला विश्वास असलेल्या अॅप्स आणि लोकांसह दोष अहवाल सामायिक करा."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"पुढील वेळी हा संदेश दर्शवा"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"दोष अहवाल"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"दोष अहवाल फाईल वाचणे शक्य झाले नाही"</string>
</resources>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index f3e4f7e..61acca3 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan pepijat mengandungi data dari pelbagai fail log sistem, termasuk maklumat peribadi dan sulit. Kongsikan laporan pepijat hanya dengan apl dan orang yang anda percayai."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tunjukkan mesej ini pada masa akan datang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan pepijat"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Fail laporan pepijat tidak boleh dibaca"</string>
</resources>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index d223bd9..4ba9037 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"အမှားအယွင်း မှတ်တမ်းမှာ ပါရှိသော အချက်အလက်များမှာ ကိုယ်ရေးကိုယ်တာ နဲ့ လုံခြုံရေး အချက်အလက်များပါဝင်သော စနစ်မှ ပြုလုပ်မှု မှတ်တမ်းများ ဖြစ်ပါသည်၊ အမှားအယွင်း မှတ်တမ်းများကို ယုံကြည်ရသော အပလီကေးရှင်းများနဲ့ လူများကိုသာ ပေးဝေပြသမှု လုပ်ပါရန်။"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ဤစာတန်းကို နောက်တစ်ခါတွင် ပြရန်"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ချို့ယွင်းမှု အစီရင်ခံစာများ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ချွတ်ယွင်းချက် အစီရင်ခံစာကို ဖတ်၍မရပါ"</string>
</resources>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index c00e2d0..c543e49 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Feilrapporter inkluderer data fra systemets forskjellige loggfiler. Dette omfatter personlig og privat informasjon. Du bør bare dele feilrapporter med apper og folk du stoler på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meldingen neste gang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Feilrapporter"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Feilrapportfilen kunne ikke leses"</string>
</resources>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index 7344889..f57112a 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूबाट व्यक्तिगत तथा नीजि सूचनासहितको डेटा रहन्छ। बग रिपोर्टहरू अनुप्रयोगहरू र तपाईँले विश्वास गरेका व्यक्तिहरूसँग मात्र साझेदारी गर्नुहोस्।"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यो सन्देश अर्को पटक देखाउनुहोस्"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्टहरू"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रिपोर्ट फाइल पढ्न सकिएन"</string>
</resources>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index b0dba4d..1519454 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Foutenrapporten"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Bestand met bugrapport kan niet worden gelezen"</string>
</resources>
diff --git a/packages/Shell/res/values-pa-rIN/strings.xml b/packages/Shell/res/values-pa-rIN/strings.xml
index a0c4cda..3f59bb9 100644
--- a/packages/Shell/res/values-pa-rIN/strings.xml
+++ b/packages/Shell/res/values-pa-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"ਬਗ ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਸਿਸਟਮ ਦੀਆਂ ਭਿੰਨ ਲੌਗ ਫਾਈਲਾਂ ਦਾ ਡਾਟਾ ਹੁੰਦਾ ਹੈ, ਨਿੱਜੀ ਅਤੇ ਪ੍ਰਾਈਵੇਟ ਜਾਣਕਾਰੀ ਸਮੇਤ। ਕੇਵਲ ਉਹਨਾਂ ਐਪਸ ਅਤੇ ਲੋਕਾਂ ਨਾਲ ਬਗ ਰਿਪੋਰਟਾਂ ਸ਼ੇਅਰ ਕਰੋ, ਜਿਹਨਾਂ ਤੇ ਤੁਸੀਂ ਭਰੋਸਾ ਕਰਦੇ ਹੋ।"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ਅਗਲੀ ਵਾਰ ਇਹ ਸੁਨੇਹਾ ਦਿਖਾਓ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ਬਗ ਰਿਪੋਰਟਾਂ"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ਬਗ ਰਿਪੋਰਟ ਫ਼ਾਈਲ ਪੜ੍ਹੀ ਨਹੀਂ ਜਾ ਸਕੀ"</string>
</resources>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index 96b8f2a..accc92e8 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Raporty o błędach zawierają dane z różnych plików dzienników systemu, w tym dane osobowe i prywatne. Udostępniaj je tylko aplikacjom i osobom, którym ufasz."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaż ten komunikat następnym razem"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Raporty o błędach"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Nie można odczytać raportu o błędzie"</string>
</resources>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index e04d600..7d4b0d3 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
</resources>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 648ef94..007d683 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de erros incluem dados de vários ficheiros de registo do sistema, nomeadamente informações pessoais e privadas. Partilhe relatórios de erros apenas com aplicações e pessoas fidedignas."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de erros"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o ficheiro de relatório de erro"</string>
</resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index e04d600..7d4b0d3 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
</resources>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index aab29b7..e7395e1 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului, inclusiv informații private și personale. Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afișați acest mesaj data viitoare"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapoarte de erori"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Fișierul cu raportul de eroare nu a putut fi citit"</string>
</resources>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index 1f1444d..c9276cf 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Отчеты об ошибках"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не удалось открыть отчет об ошибке"</string>
</resources>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index 4244f2b..937c1bc 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"පුද්ගලික සහ පෞද්ගලික තොරතුරු ඇතුළත්ව පද්ධතියේ විවිධ ලොග් ගොනු වල දත්ත දෝෂ වාර්තාවේ අඩංගු වේ. ඔබට විශ්වාසවන්ත යෙදුම් සහ පුද්ගලයින් සමඟ පමණක් දෝෂ වාර්තා බෙදා ගන්න."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ඊළඟ වෙලාවේ මෙම පණිවිඩය පෙන්වන්න"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"දෝෂ වාර්තා"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"දෝෂ වාර්තා ගොනුව කියවීමට නොහැකි විය"</string>
</resources>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index 4228dd3..cd52efc 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Správy o chybách obsahujú údaje z rôznych súborov denníkov systému vrátane osobných a súkromných informácií. Zdieľajte ich iba s dôveryhodnými aplikáciami a ľuďmi."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobraziť túto správu nabudúce"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hlásenia chýb"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Súbor s hlásením chyby sa nepodarilo prečítať"</string>
</resources>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index 8c3bedc..25553f2 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Poročila o napakah vsebujejo podatke iz različnih dnevniških datotek sistema, vključno z osebnimi in zasebnimi podatki. Poročila o napakah delite samo z aplikacijami in ljudmi, ki jim zaupate."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži to sporočilo naslednjič"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Poročila o napakah"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Datoteke s poročilom o napakah ni bilo mogoče prebrati"</string>
</resources>
diff --git a/packages/Shell/res/values-sq-rAL/strings.xml b/packages/Shell/res/values-sq-rAL/strings.xml
index 8f252a0..add53d8 100644
--- a/packages/Shell/res/values-sq-rAL/strings.xml
+++ b/packages/Shell/res/values-sq-rAL/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Raportet e gabimeve përmbajnë të dhëna nga skedarë të ndryshëm ditarësh sistemi, përfshi informacione personale dhe private. Shpërndaji publikisht raportet e gabimeve vetëm me aplikacionet dhe personat që iu beson."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tregoje këtë mesazh herën tjetër"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Raportet e gabimeve"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Skedari i raportimit të defektit në kod nuk mund të lexohej."</string>
</resources>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index 763bd2b..bb60f3f 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, укључујући личне и приватне податке. Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ову поруку следећи пут"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаји о грешкама"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Датотека извештаја о грешци не може да се прочита"</string>
</resources>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index 8b72938..dd23e16 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Felrapporter"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Det gick inte att läsa felrapporten"</string>
</resources>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 0d12cc9..3773cd7 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Ripoti ya hitilafu ina data kutoka kwenye faili za kumbukumbu mbalimbali za mfumo, pamoja na maelezo ya kibinafsi na faragha. Shiriki ripoti ya hitilafu na programu na watu unaowaamini pekee."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Onyesha ujumbe huu wakati mwingine"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Faili ya ripoti ya hitilafu haikusomwa"</string>
</resources>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index cd0462b..244c929 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"பிழை அறிக்கைகளில், சொந்த வாழ்க்கை மற்றும் தனிப்பட்ட தகவல் உள்பட கணினியின் பல்வேறு பதிவுகளில் உள்ள தரவு இருக்கும். நீங்கள் நம்பும் பயன்பாடுகள் மற்றும் நபர்களுடன் மட்டும் பிழை அறிக்கைகளைப் பகிரவும்."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"இந்தச் செய்தியை அடுத்த முறைக் காட்டு"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"பிழை அறிக்கைகள்"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"பிழை அறிக்கையைப் படிக்க முடியவில்லை"</string>
</resources>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index 127f602..cd9b4b7 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"బగ్ నివేదికలు వ్యక్తిగతమైన మరియు రహస్యమైన సమాచారంతో సహా సిస్టమ్ యొక్క విభిన్న లాగ్ ఫైల్ల్లోని డేటాను కలిగి ఉంటాయి. కనుక బగ్ నివేదికలను మీరు విశ్వసించే అనువర్తనాలు మరియు వ్యక్తులతో మాత్రమే భాగస్వామ్యం చేయండి."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"తదుపరిసారి ఈ సందేశాన్ని చూపు"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"బగ్ నివేదికలు"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"బగ్ నివేదిక ఫైల్ను చదవడం సాధ్యపడలేదు"</string>
</resources>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index 2bc8f0d..35b7ec9 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"รายงานข้อบกพร่องมีข้อมูลจากไฟล์บันทึกต่างๆ ของระบบ รวมถึงข้อมูลส่วนตัว แชร์รายงานข้อบกพร่องกับแอปและบุคคลที่คุณไว้ใจเท่านั้น"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"แสดงข้อความนี้ในครั้งต่อไป"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"รายงานข้อบกพร่อง"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"ไม่สามารถอ่านไฟล์รายงานข้อบกพร่อง"</string>
</resources>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index a5c0e8a..21df206 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Mga ulat sa bug"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Hindi mabasa ang file ng pag-uulat ng bug"</string>
</resources>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index 67390b7..fa8b82b 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu iletiyi göster"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hata raporları"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Hata raporu dosyası okunamadı"</string>
</resources>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index f8f1798..ba667f2 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Звіти про помилки містять дані з різних файлів журналу системи, зокрема особисті та конфіденційні. Надсилайте звіт про помилки лише тим, кому довіряєте."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показати це повідомлення наступного разу"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не вдалося прочитати звіт про помилки"</string>
</resources>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index 73d6877..255aaf8 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"بَگ رپورٹس میں سسٹم کی مختلف لاگ فائلوں سے ڈیٹا شامل ہوتا ہے، بشمول ذاتی اور نجی معلومات۔ بَگ رپورٹس کا اشتراک صرف اپنے بھروسے مند ایپس اور لوگوں کے ساتھ کریں۔"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"یہ پیغام اگلی بار دکھائیں"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"بگ رپورٹس"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"بگ رپورٹ فائل پڑھی نہیں جا سکی"</string>
</resources>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index b221171..998387e 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Xatolik hisobotlari tizimdagi har xil jurnal fayllardagi ma’lumotlarni, shuningdek, shaxsiy hamda maxfiy ma’lumotlarni o‘z ichiga oladi. Xatolik hisobotlarini faqat ishonchli dasturlar va odamlar bilan bo‘lishing."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ushbu xabar keyingi safar ko‘rsatilsin"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Xatoliklar hisoboti"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Xatoliklar hisoboti faylini o‘qib bo‘lmadi"</string>
</resources>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 16a7df9..5c11b13 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, bao gồm cả thông tin cá nhân và riêng tư. Chỉ chia sẻ báo cáo lỗi với các ứng dụng và những người mà bạn tin tưởng."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Hiển thị thông báo này vào lần tới"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Báo cáo lỗi"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Không thể đọc tệp báo cáo lỗi"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 17fdedd..0a2f81b 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"错误报告"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"无法读取错误报告文件"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index 0f70bab..227d937 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再顯示這則訊息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"無法讀取錯誤報告檔案"</string>
</resources>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index d7f7507..b9f8ee8 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告的資料來自系統各個紀錄檔,包括個人和私密資訊。請務必只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次仍顯示這則訊息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"無法讀取錯誤報告檔案"</string>
</resources>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index c82fb93..b675858 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -23,4 +23,5 @@
<string name="bugreport_confirm" msgid="5130698467795669780">"Imibiko yeziphazamisi iqukethe idatha yamafayela wokungena ahlukile wesistimu, afaka ulwazi lomuntu siqu noma lobumfihlo. Yabelana kuphela ngemibiko yeziphazamisi nezinhlelo zokusebenza nabantu obathembayo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bonisa lo mlayezo ngesikhathi esilandelayo"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Imibiko yeziphazamiso"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ifayela lombiko wesiphazamso alikwazanga ukufundwa"</string>
</resources>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 3db0848..4469d38 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -33,4 +33,8 @@
<!-- Title for documents backend that offers bugreports. -->
<string name="bugreport_storage_title">Bug reports</string>
+
+ <!-- Toast message sent when the bugreport file could be read. -->
+ <string name="bugreport_unreadable_text">Bug report file could not be read</string>
+
</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index e90a3b5..c264372 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -37,6 +37,7 @@
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Patterns;
+import android.widget.Toast;
import com.google.android.collect.Lists;
import libcore.io.Streams;
@@ -105,6 +106,13 @@
*/
private void triggerLocalNotification(final Context context, final File bugreportFile,
final File screenshotFile) {
+ if (!bugreportFile.exists() || !bugreportFile.canRead()) {
+ Log.e(TAG, "Could not read bugreport file " + bugreportFile);
+ Toast.makeText(context, context.getString(R.string.bugreport_unreadable_text),
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
boolean isPlainText = bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
@@ -141,10 +149,12 @@
intent.putExtra(Intent.EXTRA_TEXT, messageBody);
final ClipData clipData = new ClipData(null, new String[] { mimeType },
new ClipData.Item(null, null, null, bugreportUri));
- clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
+ final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri);
+ if (screenshotUri != null) {
+ clipData.addItem(new ClipData.Item(null, null, null, screenshotUri));
+ attachments.add(screenshotUri);
+ }
intent.setClipData(clipData);
-
- final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri, screenshotUri);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments);
final Account sendToAccount = findSendToAccount(context);
@@ -162,8 +172,8 @@
File screenshotFile) {
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
- final Uri bugreportUri = FileProvider.getUriForFile(context, AUTHORITY, bugreportFile);
- final Uri screenshotUri = FileProvider.getUriForFile(context, AUTHORITY, screenshotFile);
+ final Uri bugreportUri = getUri(context, bugreportFile);
+ final Uri screenshotUri = getUri(context, screenshotFile);
Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
Intent notifIntent;
@@ -272,6 +282,10 @@
return foundAccount;
}
+ private static Uri getUri(Context context, File file) {
+ return file != null ? FileProvider.getUriForFile(context, AUTHORITY, file) : null;
+ }
+
private static File getFileExtra(Intent intent, String key) {
final String path = intent.getStringExtra(key);
if (path != null) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index be5c0fe..6fda2c6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -145,7 +145,9 @@
android:icon="@drawable/icon"
android:process="com.android.systemui"
android:supportsRtl="true"
- android:theme="@style/systemui_theme">
+ android:theme="@style/systemui_theme"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<!-- Keep theme in sync with SystemUIApplication.onCreate().
Setting the theme on the application does not affect views inflated by services.
The application theme is set again from onCreate to take effect for those views. -->
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 368f9f7..f2920e5 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -12,12 +12,23 @@
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
--keep class com.android.systemui.recents.*
-keepclassmembers class ** {
public void onBusEvent(**);
public void onInterprocessBusEvent(**);
}
-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
- public <init>(android.os.Bundle);
-}
\ No newline at end of file
+ public <init>(android.os.Bundle);
+}
+
+-keep class com.android.systemui.recents.views.TaskStackLayoutAlgorithm {
+ public float getFocusState();
+ public void setFocusState(float);
+}
+
+-keep class com.android.systemui.recents.views.TaskView {
+ public int getDim();
+ public void setDim(int);
+ public float getTaskProgress();
+ public void setTaskProgress(float);
+}
diff --git a/packages/SystemUI/res/anim/recents_fast_toggle_app_home_exit.xml b/packages/SystemUI/res/anim/recents_fast_toggle_app_home_exit.xml
new file mode 100644
index 0000000..69edcc7
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_fast_toggle_app_home_exit.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+<!-- Recents Activity -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="250"/>
+</set>
diff --git a/packages/SystemUI/res/drawable/ic_send.xml b/packages/SystemUI/res/drawable/ic_send.xml
new file mode 100644
index 0000000..b1c7914
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_send.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.02,42.0L46.0,24.0 4.02,6.0 4.0,20.0l30.0,4.0 -30.0,4.0z"/>
+ <path
+ android:pathData="M0 0h48v48H0z"
+ android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/recents_dismiss_button.xml b/packages/SystemUI/res/layout/recents_dismiss_button.xml
deleted file mode 100644
index 6a2f782..0000000
--- a/packages/SystemUI/res/layout/recents_dismiss_button.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Extends Framelayout -->
-<com.android.systemui.statusbar.DismissView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/recents_dismiss_all_button_size"
- android:visibility="gone"
- android:clipChildren="false"
- android:clipToPadding="false">
- <com.android.systemui.statusbar.DismissViewButton
- android:id="@+id/dismiss_text"
- android:layout_width="@dimen/recents_dismiss_all_button_size"
- android:layout_height="@dimen/recents_dismiss_all_button_size"
- android:layout_gravity="end"
- android:alpha="1.0"
- android:background="@drawable/recents_button_bg"
- android:contentDescription="@string/recents_dismiss_all_message"/>
-</com.android.systemui.statusbar.DismissView>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 8ca5634..74092c1 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -16,31 +16,61 @@
~ limitations under the License
-->
-<!-- FrameLayout -->
+<!-- LinearLayout -->
<com.android.systemui.statusbar.policy.RemoteInputView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/systemui_theme_light"
+ android:theme="@style/systemui_theme_remote_input"
+ android:id="@+id/remote_input"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:paddingStart="4dp"
- android:paddingEnd="2dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp"
android:paddingBottom="4dp"
android:paddingTop="2dp">
<view class="com.android.systemui.statusbar.policy.RemoteInputView$RemoteEditText"
android:id="@+id/remote_input_text"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:paddingEnd="12dp"
+ android:gravity="start|center_vertical"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textColor="#deffffff"
+ android:textSize="16sp"
+ android:background="@null"
android:singleLine="true"
+ android:ellipsize="start"
android:imeOptions="actionSend" />
- <ProgressBar
- android:id="@+id/remote_input_progress"
- android:layout_width="match_parent"
+ <FrameLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:visibility="invisible"
- android:indeterminate="true"
- style="?android:attr/progressBarStyleHorizontal" />
+ android:layout_gravity="center_vertical">
+
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:id="@+id/remote_input_send"
+ android:src="@drawable/ic_send"
+ android:tint="@android:color/white"
+ android:tintMode="src_atop"
+ android:background="@drawable/ripple_drawable" />
+
+ <ProgressBar
+ android:id="@+id/remote_input_progress"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:visibility="invisible"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyleSmall" />
+
+ </FrameLayout>
</com.android.systemui.statusbar.policy.RemoteInputView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 01d5e05..6c9cc1a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Wys batteryvlakpersentasie binne die statusbalkikoon wanneer dit nie laai nie"</string>
<string name="quick_settings" msgid="10042998191725428">"Kitsinstellings"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusbalk"</string>
+ <string name="overview" msgid="4018602013895926956">"Oorsig"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demonstrasiemodus"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Aktiveer demonstrasiemodus"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Wys demonstrasiemodus"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Wys horlosiesekondes op die statusbalk. Sal batterylewe dalk beïnvloed."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Herrangskik Kitsinstellings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Wys helderheid in Kitsinstellings"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Aktiveer vinnige wissel"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Aktiveer blaai met die Oorsig-knoppie"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimenteel"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Skakel Bluetooth aan?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Jy moet Bluetooth aanskakel om jou sleutelbord aan jou tablet te koppel."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 8999d7c..e7346a1 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ኃይል በማይሞላበት ጊዜ በሁነታ አሞሌ አዶ ውስጥ የባትሪ ደረጃ መቶኛን አሳይ"</string>
<string name="quick_settings" msgid="10042998191725428">"ፈጣን ቅንብሮች"</string>
<string name="status_bar" msgid="4877645476959324760">"የሁኔታ አሞሌ"</string>
+ <string name="overview" msgid="4018602013895926956">"አጠቃላይ እይታ"</string>
<string name="demo_mode" msgid="2389163018533514619">"የቅንጭብ ማሳያ ሁነታ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"የማሳያ ሁነታውን ያንቁ"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ማሳያ ሁነታን አሳይ"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"የሰዓት ሰከንዶችን በሁኔታ አሞሌ ውስጥ አሳይ። በባትሪ ዕድሜ ላይ ተጽዕኖ ሊኖርው ይችል ይሆናል።"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ፈጣን ቅንብሮችን ዳግም ያደራጁ"</string>
<string name="show_brightness" msgid="6613930842805942519">"በፈጣን ቅንብሮች ውስጥ ብሩህነትን አሳይ"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"ፈጣን ቅይይርን አንቃ"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"በአጠቃላይ እይታ አዝራር በኩል ገጽ ቁጥር አሰጣጥን አንቃ"</string>
<string name="experimental" msgid="6198182315536726162">"የሙከራ"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ብሉቱዝ ይብራ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"የቁልፍ ሰሌዳዎን ከእርስዎ ጡባዊ ጋር ለማገናኘት በመጀመሪያ ብሉቱዝን ማብራት አለብዎት።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index f677972..d8af21c 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -413,6 +413,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"عرض نسبة مستوى البطارية داخل رمز شريط الحالة أثناء عدم الشحن"</string>
<string name="quick_settings" msgid="10042998191725428">"الإعدادات السريعة"</string>
<string name="status_bar" msgid="4877645476959324760">"شريط الحالة"</string>
+ <string name="overview" msgid="4018602013895926956">"نظرة عامة"</string>
<string name="demo_mode" msgid="2389163018533514619">"الوضع التجريبي"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"تمكين الوضع التجريبي"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"عرض الوضع التجريبي"</string>
@@ -441,6 +442,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"عرض ثواني الساعة في شريط الحالة. قد يؤثر ذلك في عمر البطارية."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"إعادة ترتيب الإعدادات السريعة"</string>
<string name="show_brightness" msgid="6613930842805942519">"عرض السطوع في الإعدادات السريعة"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"تمكين التبديل السريع"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"تمكين الترحيل عبر زر \"نظرة عامة\""</string>
<string name="experimental" msgid="6198182315536726162">"إعدادات تجريبية"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"تشغيل البلوتوث؟"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"لتوصيل لوحة المفاتيح بالجهاز اللوحي، يلزمك تشغيل بلوتوث أولاً."</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index fb92d6b..a2f1bbb 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Elektrik şəbəsinə qoşulu olmayan zaman batareya səviyyəsini status paneli ikonası daxilində göstərin"</string>
<string name="quick_settings" msgid="10042998191725428">"Sürətli Ayarlar"</string>
<string name="status_bar" msgid="4877645476959324760">"Status paneli"</string>
+ <string name="overview" msgid="4018602013895926956">"İcmal"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo rejimi"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demo rejimini aktiv edin"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demo rejimini göstərin"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Saatın saniyəsini status panelində göstərin. Batareyaya təsir edə bilər."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Sürətli Ayarları yenidən tənzimləyin"</string>
<string name="show_brightness" msgid="6613930842805942519">"Sürətli ayarlarda parlaqlılığı göstərin"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Sürətli keçidi aktiv edin"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Baxış düyməsi vasitəsilə səhifələməni aktiv edin"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth aktivləşsin?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Tabletinizlə klaviaturaya bağlanmaq üçün ilk olaraq Bluetooth\'u aktivləşdirməlisiniz."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index b7f96a1..51b8cd1 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Показване на процента на нивото на батерията в иконата на лентата на състоянието, когато не се зарежда"</string>
<string name="quick_settings" msgid="10042998191725428">"Бързи настройки"</string>
<string name="status_bar" msgid="4877645476959324760">"Лента на състоянието"</string>
+ <string name="overview" msgid="4018602013895926956">"Общ преглед"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демонстрационен режим"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Активиране на демонстрационния режим"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Показване на демонстрационния режим"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показване на секундите на часовника в лентата на състоянието. Може да се отрази на живота на батерията."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Пренареждане на бързите настройки"</string>
<string name="show_brightness" msgid="6613930842805942519">"Показване на яркостта от бързите настройки"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Активиране на бързото превключване"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Активиране на разделянето на страници чрез бутона за общ преглед"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментални"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Да се включи ли Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"За да свържете клавиатурата с таблета си, първо трябва да включите Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 6a93724..86357bd 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"যখন চার্জ করা হবে না তখন স্থিতি দন্ডের আইকনের ভিতরে ব্যাটারি স্তরের শতকার হার দেখায়"</string>
<string name="quick_settings" msgid="10042998191725428">"দ্রুত সেটিংস"</string>
<string name="status_bar" msgid="4877645476959324760">"স্থিতি দন্ড"</string>
+ <string name="overview" msgid="4018602013895926956">"এক নজরে"</string>
<string name="demo_mode" msgid="2389163018533514619">"ডেমো মোড"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ডেমো মোড সক্ষম করুন"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ডেমো মোড দেখান"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"স্থিতি দন্ডে ঘড়ির সেকেন্ড দেখায়৷ ব্যাটারি লাইফকে প্রভাবিত করতে পারে৷"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"দ্রুত সেটিংসে পুনরায় সাজান"</string>
<string name="show_brightness" msgid="6613930842805942519">"দ্রুত সেটিংসে উজ্জ্বলতা দেখান"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"\'দ্রুত টগল করা\'র ব্যবস্থাটি সক্ষম করুন"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"\'এক নজরে\' বোতামের মাধ্যমে পেজিং সক্ষম করুন"</string>
<string name="experimental" msgid="6198182315536726162">"পরীক্ষামূলক"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth চালু করবেন?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"আপনার ট্যাবলেটের সাথে আপনার কীবোর্ড সংযুক্ত করতে, আপনাকে প্রথমে Bluetooth চালু করতে হবে।"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 0da503a..950a8e5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra el percentatge del nivell de bateria dins de la icona de la barra d\'estat quan no s\'estigui carregant"</string>
<string name="quick_settings" msgid="10042998191725428">"Configuració ràpida"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra d\'estat"</string>
+ <string name="overview" msgid="4018602013895926956">"Visió general"</string>
<string name="demo_mode" msgid="2389163018533514619">"Mode de demostració"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activa el mode de demostració"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostra el mode de demostració"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza Configuració ràpida"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a Configuració ràpida"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Activa el canvi ràpid"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Activa la paginació mitjançant el botó Visió general"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vols activar el Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c9e8a83..7d362d5 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Když neprobíhá nabíjení, zobrazit v ikoně na stavovém řádku procento nabití baterie"</string>
<string name="quick_settings" msgid="10042998191725428">"Rychlé nastavení"</string>
<string name="status_bar" msgid="4877645476959324760">"Stavový řádek"</string>
+ <string name="overview" msgid="4018602013895926956">"Přehled"</string>
<string name="demo_mode" msgid="2389163018533514619">"Ukázkový režim"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Zapnout ukázkový režim"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Zobrazit ukázkový režim"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Na stavovém řádku se bude zobrazovat sekundová ručička. Může být ovlivněna výdrž baterie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Změnit uspořádání Rychlého nastavení"</string>
<string name="show_brightness" msgid="6613930842805942519">"Zobrazit jas v Rychlém nastavení"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Povolit rychlé přepínání"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Povolit stránkování pomocí tlačítka Přehled"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentální"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Zapnout Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Chcete-li klávesnici připojit k tabletu, nejdříve musíte zapnout Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a9931ce..e889dbf 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Vis procenttallet for batteriniveauet i ikonet for statusbjælken, når der ikke oplades"</string>
<string name="quick_settings" msgid="10042998191725428">"Hurtige indstillinger"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusbjælke"</string>
+ <string name="overview" msgid="4018602013895926956">"Oversigt"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demotilstand"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Aktivér Demotilstand"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Vis Demotilstand"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statuslinjen. Dette kan påvirke batteriets levetid."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Hurtige indstillinger"</string>
<string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Hurtige indstillinger"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Aktivér hurtigt skift"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Aktivér sideopdeling via knappen Oversigt"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentel"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå Bluetooth til?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8a7fa5e..6ac2790 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prozentzahl für Akkustand in Statusleistensymbol anzeigen, wenn das Gerät nicht geladen wird"</string>
<string name="quick_settings" msgid="10042998191725428">"Schnelleinstellungen"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusleiste"</string>
+ <string name="overview" msgid="4018602013895926956">"Übersicht"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demomodus"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demomodus aktivieren"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demomodus anzeigen"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Uhrsekunden in der Statusleiste anzeigen. Kann sich auf die Akkulaufzeit auswirken."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Schnelleinstellungen neu anordnen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Helligkeit in den Schnelleinstellungen anzeigen"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Schnelles Wechseln aktivieren"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Seitenlayout über die Schaltfläche \"Übersicht\" aktivieren"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentell"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth aktivieren?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Zum Verbinden von Tastatur und Tablet muss Bluetooth aktiviert sein."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 47eb46f..c6c61e7 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Εμφάνιση ποσοστού επιπέδου μπαταρίας μέσα στο εικονίδιο της γραμμής κατάστασης όταν δεν γίνεται φόρτιση"</string>
<string name="quick_settings" msgid="10042998191725428">"Γρήγορες ρυθμίσεις"</string>
<string name="status_bar" msgid="4877645476959324760">"Γραμμή κατάστασης"</string>
+ <string name="overview" msgid="4018602013895926956">"Επισκόπηση"</string>
<string name="demo_mode" msgid="2389163018533514619">"Λειτουργία επίδειξης"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ενεργοποίηση λειτουργίας επίδειξης"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Εμφάνιση λειτουργίας επίδειξης"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Εμφάνιση δευτερολέπτων ρολογιού στη γραμμή κατάστασης. Ενδέχεται να επηρεάσει τη διάρκεια ζωής της μπαταρίας."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Αναδιάταξη Γρήγορων ρυθμίσεων"</string>
<string name="show_brightness" msgid="6613930842805942519">"Εμφάνιση φωτεινότητας στις Γρήγορες ρυθμίσεις"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Ενεργοποίηση γρήγορης εναλλαγής"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Ενεργοποίηση σελιδοποίησης μέσω του κουμπιού \"Επισκόπηση\""</string>
<string name="experimental" msgid="6198182315536726162">"Σε πειραματικό στάδιο"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Ενεργοποίηση Bluetooth;"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Για να συνδέσετε το πληκτρολόγιο με το tablet σας, θα πρέπει πρώτα να ενεργοποιήσετε το Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f257c7f..7efb7eb 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
<string name="quick_settings" msgid="10042998191725428">"Quick Settings"</string>
<string name="status_bar" msgid="4877645476959324760">"Status bar"</string>
+ <string name="overview" msgid="4018602013895926956">"Overview"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo mode"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Enable demo mode"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Show demo mode"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Enable fast toggle"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Enable paging via the Overview button"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f257c7f..7efb7eb 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
<string name="quick_settings" msgid="10042998191725428">"Quick Settings"</string>
<string name="status_bar" msgid="4877645476959324760">"Status bar"</string>
+ <string name="overview" msgid="4018602013895926956">"Overview"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo mode"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Enable demo mode"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Show demo mode"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Enable fast toggle"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Enable paging via the Overview button"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f257c7f..7efb7eb 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Show battery level percentage inside the status bar icon when not charging"</string>
<string name="quick_settings" msgid="10042998191725428">"Quick Settings"</string>
<string name="status_bar" msgid="4877645476959324760">"Status bar"</string>
+ <string name="overview" msgid="4018602013895926956">"Overview"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo mode"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Enable demo mode"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Show demo mode"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Show clock seconds in the status bar. May impact battery life."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
<string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Enable fast toggle"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Enable paging via the Overview button"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Turn on Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"To connect your keyboard with your tablet, you first have to turn on Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 608f73c..5911314 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentaje del nivel de batería en el ícono de la barra de estado cuando no se está cargando"</string>
<string name="quick_settings" msgid="10042998191725428">"Configuración rápida"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de estado"</string>
+ <string name="overview" msgid="4018602013895926956">"Recientes"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo demostración"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activar el modo de demostración"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Ver en modo de demostración"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar la duración de la batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar la Configuración rápida"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar el brillo en la Configuración rápida"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Habilitar la activación rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Habilita la paginación a través del botón Recientes"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"¿Activar Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar el teclado con la tablet, primero debes activar Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index aa4fd56..7deaa18 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar el porcentaje del nivel de batería en el icono de la barra de estado cuando no se esté cargando"</string>
<string name="quick_settings" msgid="10042998191725428">"Ajustes rápidos"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de estado"</string>
+ <string name="overview" msgid="4018602013895926956">"Visión general"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo de demostración"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Habilitar modo de demostración"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demostración"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Muestra los segundos del reloj en la barra de estado. Puede afectar a la duración de la batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Ajustes rápidos"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Ajustes rápidos"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Habilitar activación rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Habilitar paginación con el botón Visión general"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"¿Activar Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para poder conectar tu teclado a tu tablet, debes activar el Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 655ee5b..4bca9b9 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Akutaseme protsendi kuvamine olekuriba ikoonil, kui akut ei laeta"</string>
<string name="quick_settings" msgid="10042998191725428">"Kiirseaded"</string>
<string name="status_bar" msgid="4877645476959324760">"Olekuriba"</string>
+ <string name="overview" msgid="4018602013895926956">"Ülevaade"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demorežiim"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demorežiimi lubamine"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Kuva demorežiim"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Olekuribal kella sekundite kuvamine. See võib mõjutada aku kasutusaega."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Korralda kiirseaded ümber"</string>
<string name="show_brightness" msgid="6613930842805942519">"Kuva kiirseadetes heledus"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Luba kiire vahetamine"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Saate lubada sirvimise nupuga Ülevaade"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentaalne"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Kas lülitada Bluetooth sisse?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Klaviatuuri ühendamiseks tahvelarvutiga peate esmalt Bluetoothi sisse lülitama."</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 5ac93c6..d906d2d 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Erakutsi bateria-mailaren ehunekoa egoera-barraren ikonoan, kargatzen ari ez denean"</string>
<string name="quick_settings" msgid="10042998191725428">"Ezarpen bizkorrak"</string>
<string name="status_bar" msgid="4877645476959324760">"Egoera-barra"</string>
+ <string name="overview" msgid="4018602013895926956">"Ikuspegi orokorra"</string>
<string name="demo_mode" msgid="2389163018533514619">"Proba modua"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Gaitu proba modua"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Erakutsi proba modua"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Erakutsi erlojuko segundoak egoera-barran. Baliteke bateria gehiago erabiltzea."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Berrantolatu ezarpen bizkorrak"</string>
<string name="show_brightness" msgid="6613930842805942519">"Erakutsi distira Ezarpen bizkorretan"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Gaitu bizkor aldatzeko aukera"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Gaitu orriz aldatzea ikuspegi orokorraren botoiaren bidez"</string>
<string name="experimental" msgid="6198182315536726162">"Esperimentala"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth eginbidea aktibatu nahi duzu?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Teklatua tabletara konektatzeko, Bluetooth eginbidea aktibatu behar duzu."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 837b727..4a41fdf 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"نمایش درصد سطح باتری در نماد نوار وضعیت، هنگامی که باتری شارژ نمیشود"</string>
<string name="quick_settings" msgid="10042998191725428">"تنظیمات سریع"</string>
<string name="status_bar" msgid="4877645476959324760">"نوار وضعیت"</string>
+ <string name="overview" msgid="4018602013895926956">"نمای کلی"</string>
<string name="demo_mode" msgid="2389163018533514619">"حالت نمایشی"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"فعال کردن حالت نمایشی"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"نمایش حالت نمایشی"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ثانیههای ساعت را در نوار وضعیت نشان میدهد. ممکن است بر ماندگاری باتری تأثیر بگذارد."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ترتیب مجدد در تنظیمات سریع"</string>
<string name="show_brightness" msgid="6613930842805942519">"نمایش روشنایی در تنظیمات سریع"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"فعال کردن جابهجایی سریع"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"فعال کردن صفحهبندی از طریق دکمه «نمای کلی»"</string>
<string name="experimental" msgid="6198182315536726162">"آزمایشی"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"بلوتوث روشن شود؟"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"برای مرتبط کردن صفحهکلید با رایانه لوحی، ابتدا باید بلوتوث را روشن کنید."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ef6a064..3c90719 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Näyttää akun varausprosentin tilapalkin kuvakkeessa, kun laitetta ei ladata."</string>
<string name="quick_settings" msgid="10042998191725428">"Pika-asetukset"</string>
<string name="status_bar" msgid="4877645476959324760">"Tilapalkki"</string>
+ <string name="overview" msgid="4018602013895926956">"Yleiskatsaus"</string>
<string name="demo_mode" msgid="2389163018533514619">"Esittelytila"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ota esittelytila käyttöön"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Näytä esittelytila"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Näytä sekunnit tilapalkin kellossa. Tämä voi vaikuttaa akun kestoon."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Järjestä pika-asetukset uudelleen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Näytä kirkkaus pika-asetuksissa"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Ota käyttöön nopea päälle/pois-toiminto."</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Ota sivutus käyttöön Yleiskatsaus-painikkeen avulla."</string>
<string name="experimental" msgid="6198182315536726162">"Kokeellinen"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Otetaanko Bluetooth käyttöön?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Jotta voit yhdistää näppäimistön tablettiisi, sinun on ensin otettava Bluetooth käyttöön."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 52d2e2a..72f3499 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Afficher le pourcentage correspondant au niveau de la pile dans l\'icône de la barre d\'état lorsque l\'appareil n\'est pas en charge."</string>
<string name="quick_settings" msgid="10042998191725428">"Paramètres rapides"</string>
<string name="status_bar" msgid="4877645476959324760">"Barre d\'état"</string>
+ <string name="overview" msgid="4018602013895926956">"Aperçu"</string>
<string name="demo_mode" msgid="2389163018533514619">"Mode Démonstration"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activer le mode Démonstration"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Afficher le mode Démonstration"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes sur l\'horloge dans la barre d\'état. Cela peut réduire l\'autonomie de la pile."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser les paramètres rapides"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans les paramètres rapides"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Activer le basculement rapide"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Activer la pagination à l\'aide du bouton Aperçu"</string>
<string name="experimental" msgid="6198182315536726162">"Fonctions expérimentales"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Activer Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Pour connecter votre clavier à votre tablette, vous devez d\'abord activer la connectivité Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 39a8dbc..c115fd1 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Affichez le pourcentage correspondant au niveau de la batterie dans l\'icône de la barre d\'état lorsque l\'appareil n\'est pas en charge."</string>
<string name="quick_settings" msgid="10042998191725428">"Configuration rapide"</string>
<string name="status_bar" msgid="4877645476959324760">"Barre d\'état"</string>
+ <string name="overview" msgid="4018602013895926956">"Aperçu"</string>
<string name="demo_mode" msgid="2389163018533514619">"Mode de démonstration"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activer le mode de démonstration"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Afficher le mode de démonstration"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes dans la barre d\'état. Cela risque de réduire l\'autonomie de la batterie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser la fenêtre de configuration rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans fenêtre de configuration rapide"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Activer le basculement rapide"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Activer la mise en page via le bouton Aperçu"</string>
<string name="experimental" msgid="6198182315536726162">"Paramètres expérimentaux"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Activer le Bluetooth ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 398a055..b1ca8f9 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentaxe do nivel de batería na icona da barra de estado cando non está en carga"</string>
<string name="quick_settings" msgid="10042998191725428">"Configuración rápida"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de estado"</string>
+ <string name="overview" msgid="4018602013895926956">"Visión xeral"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo de demostración"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activar modo de demostración"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demostración"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra os segundos do reloxo na barra de estado. Pode influír na duración da batería."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Configuración rápida"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Configuración rápida"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Activar alternancia rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Activar paxinación a través do botón Visión xeral"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Queres activar o Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teu teclado co tablet, primeiro tes que activar o Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 9ecaa75..db28348 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"જ્યારે ચાર્જ ન થઈ રહ્યું હોય ત્યારે સ્થિતિ બાર આયકનની અંદર બૅટરી સ્તર ટકા બતાવો"</string>
<string name="quick_settings" msgid="10042998191725428">"ઝડપી સેટિંગ્સ"</string>
<string name="status_bar" msgid="4877645476959324760">"સ્થિતિ બાર"</string>
+ <string name="overview" msgid="4018602013895926956">"વિહંગાવલોકન"</string>
<string name="demo_mode" msgid="2389163018533514619">"ડેમો મોડ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ડેમો મોડ સક્ષમ કરો"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ડેમો મોડ બતાવો"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ઘડિયાળ સેકન્ડ સ્થિતિ બારમાં બતાવો. બૅટરીની આવરદા પર અસર કરી શકે છે."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ઝડપી સેટિંગ્સને ફરીથી ગોઠવો"</string>
<string name="show_brightness" msgid="6613930842805942519">"ઝડપી સેટિંગ્સમાં તેજ બતાવો"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"ઝડપી ટૉગલ સક્ષમ કરો"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"વિહંગાવલોકન બટન મારફત પેજિંગ સક્ષમ કરો"</string>
<string name="experimental" msgid="6198182315536726162">"પ્રાયોગિક"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ચાલુ કરવુ છે?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"તમારા ટેબ્લેટ સાથે કીબોર્ડ કનેક્ટ કરવા માટે, તમારે પહેલાં Bluetooth ચાલુ કરવાની જરૂર પડશે."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 62ef606..c7c87bd 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"जब चार्ज नहीं किया जा रहा हो तब स्थिति बार आइकन में बैटरी स्तर का प्रतिशत दिखाएं"</string>
<string name="quick_settings" msgid="10042998191725428">"तेज़ सेटिंग"</string>
<string name="status_bar" msgid="4877645476959324760">"स्थिति बार"</string>
+ <string name="overview" msgid="4018602013895926956">"अवलोकन"</string>
<string name="demo_mode" msgid="2389163018533514619">"डेमो मोड"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"डेमो मोड सक्षम करें"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"डेमो मोड दिखाएं"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्थिति बार में घड़ी के सेकंड दिखाएं. इससे बैटरी के जीवनकाल पर प्रभाव पड़ सकता है."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को पुन: व्यवस्थित करें"</string>
<string name="show_brightness" msgid="6613930842805942519">"त्वरित सेटिंग में स्क्रीन की रोशनी दिखाएं"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"तेज़ टॉगल सक्षम करें"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"अवलोकन बटन के माध्यम से पेजिंग सक्षम करें"</string>
<string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटूथ चालू करें?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"अपने कीबोर्ड को अपने टैबलेट से कनेक्ट करने के लिए, आपको पहले ब्लूटूथ चालू करना होगा."</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index dafc96f..ac1ce61 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -410,6 +410,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikazivanje postotka razine baterije na ikoni trake statusa kada se ne puni"</string>
<string name="quick_settings" msgid="10042998191725428">"Brze postavke"</string>
<string name="status_bar" msgid="4877645476959324760">"Traka statusa"</string>
+ <string name="overview" msgid="4018602013895926956">"Pregled"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo način"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Omogući demo način"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Prikaži demo način"</string>
@@ -438,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikazuju se sekunde na satu na traci statusa. Može utjecati na trajanje baterije."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Promijeni raspored Brzih postavki"</string>
<string name="show_brightness" msgid="6613930842805942519">"Prikaži svjetlinu u Brzim postavkama"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Omogući brzo prebacivanje"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Omogućivanje pregledavanja stranica gumbom Pregled"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentalno"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Želite li uključiti Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Da biste povezali tipkovnicu s tabletom, morate uključiti Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index cf08b55..db4adc3 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Az akkumulátor töltöttségi szintjének megjelenítése az állapotsori ikonban, amikor az eszköz nem töltődik"</string>
<string name="quick_settings" msgid="10042998191725428">"Gyorsbeállítások"</string>
<string name="status_bar" msgid="4877645476959324760">"Állapotsor"</string>
+ <string name="overview" msgid="4018602013895926956">"Áttekintés"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demó mód"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demó mód engedélyezése"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demó mód megjelenítése"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Másodpercek megjelenítése az állapotsor óráján. Ez hatással lehet az akkumulátor üzemidejére."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Gyorsbeállítások átrendezése"</string>
<string name="show_brightness" msgid="6613930842805942519">"Fényerő megjelenítése a gyorsbeállításokban"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Gyors váltás engedélyezése"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Oldalkezelés engedélyezése az Áttekintés gombbal"</string>
<string name="experimental" msgid="6198182315536726162">"Kísérleti"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Engedélyezi a Bluetooth-kapcsolatot?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Ha a billentyűzetet csatlakoztatni szeretné táblagépéhez, először engedélyeznie kell a Bluetooth-kapcsolatot."</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index b4a265d..82e5742 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Ցուցադրել մարտկոցի լիցքավորման տոկոսայնությունը կարգավիճակի գոտու պատկերակի վրա, երբ այն չի լիցքավորվում"</string>
<string name="quick_settings" msgid="10042998191725428">"Արագ կարգավորումներ"</string>
<string name="status_bar" msgid="4877645476959324760">"Կարգավիճակի գոտի"</string>
+ <string name="overview" msgid="4018602013895926956">"Համատեսք"</string>
<string name="demo_mode" msgid="2389163018533514619">"Ցուցադրական ռեժիմ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ցուցադրական ռեժիմի միացում"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Ցուցադրական ռեժիմի ցուցադրում"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ցույց տալ ժամացույցի վայրկյանները կարգավիճակի տողում: Կարող է ազդել մարտկոցի աշխատանքի ժամանակի վրա:"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Վերադասավորել Արագ կարգավորումները"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ցույց տալ պայծառությունն Արագ կարգավորումներում"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Միացնել արագ փոխարկումը"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Միացնել Համատեսք կոճակի միջոցով թերթումը"</string>
<string name="experimental" msgid="6198182315536726162">"Փորձնական"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Միացնե՞լ Bluetooth-ը:"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Ստեղնաշարը ձեր պլանշետին միացնելու համար նախ անհրաժեշտ է միացնել Bluetooth-ը:"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 6d6c1c7..6226603 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Tampilkan persentase tingkat baterai dalam ikon bilah status saat tidak mengisi daya"</string>
<string name="quick_settings" msgid="10042998191725428">"Setelan Cepat"</string>
<string name="status_bar" msgid="4877645476959324760">"Bilah status"</string>
+ <string name="overview" msgid="4018602013895926956">"Ringkasan"</string>
<string name="demo_mode" msgid="2389163018533514619">"Mode demo"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Aktifkan mode demo"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Tampilkan mode demo"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tampilkan detik jam di bilah status. Dapat memengaruhi masa pakai baterai."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Atur Ulang Setelan Cepat"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tampilkan kecerahan di Setelan Cepat"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Mengaktifkan pengalih cepat"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Mengaktifkan tampilan laman melalui tombol Ringkasan"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Aktifkan Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Untuk menghubungkan keyboard dengan tablet, terlebih dahulu aktifkan Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 8e7711e4..506b998 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Sýna rafhlöðustöðuna í stöðustikunni þegar tækið er ekki í hleðslu"</string>
<string name="quick_settings" msgid="10042998191725428">"Flýtistillingar"</string>
<string name="status_bar" msgid="4877645476959324760">"Stöðustika"</string>
+ <string name="overview" msgid="4018602013895926956">"Yfirlit"</string>
<string name="demo_mode" msgid="2389163018533514619">"Prufustilling"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Kveikja á prufustillingu"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Sýna prufustillingu"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Sýna sekúndur á klukku í stöðustikunni. Getur haft áhrif á endingu rafhlöðu."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Endurraða flýtistillingum"</string>
<string name="show_brightness" msgid="6613930842805942519">"Sýna birtustig í flýtistillingum"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Kveikja á flýtiskiptingum"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Virkja síðuskoðun með yfirlitshnappinum"</string>
<string name="experimental" msgid="6198182315536726162">"Tilraunastillingar"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Kveikja á Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Til að geta tengt lyklaborðið við spjaldtölvuna þarftu fyrst að kveikja á Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f12e626..565a4de 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostra la percentuale di carica della batteria nell\'icona della barra di stato quando non è in carica"</string>
<string name="quick_settings" msgid="10042998191725428">"Impostazioni rapide"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra di stato"</string>
+ <string name="overview" msgid="4018602013895926956">"Panoramica"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modalità demo"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Attiva modalità demo"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostra modalità demo"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra i secondi nell\'orologio nella barra di stato. Ciò potrebbe ridurre la durata della carica della batteria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Riorganizza Impostazioni rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostra luminosità in Impostazioni rapide"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Abilita attivazione/disattivazione veloce"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Attiva il paging tramite il pulsante Panoramica"</string>
<string name="experimental" msgid="6198182315536726162">"Sperimentali"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Attivare il Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c168f7d..b05c12b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"הצג את אחוז עוצמת הסוללה בתוך הסמל שבשורת הסטטוס כשהמכשיר אינו בטעינה"</string>
<string name="quick_settings" msgid="10042998191725428">"הגדרות מהירות"</string>
<string name="status_bar" msgid="4877645476959324760">"שורת סטטוס"</string>
+ <string name="overview" msgid="4018602013895926956">"סקירה"</string>
<string name="demo_mode" msgid="2389163018533514619">"מצב הדגמה"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"הפעל מצב הדגמה"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"הצג מצב הדגמה"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"הצג שניות בשעון בשורת הסטטוס. פעולה זו עשויה להשפיע על אורך חיי הסוללה."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"סידור מחדש של הגדרות מהירות"</string>
<string name="show_brightness" msgid="6613930842805942519">"הצג בהירות בהגדרות מהירות"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"הפעל החלפת מצב מהירה"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"הפעל דפדוף באמצעות לחצן הסקירה"</string>
<string name="experimental" msgid="6198182315536726162">"ניסיוניות"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"האם להפעיל את ה-Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f053ee4..adbf701 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"充電していないときには電池残量の割合をステータスバーアイコンに表示する"</string>
<string name="quick_settings" msgid="10042998191725428">"クイック設定"</string>
<string name="status_bar" msgid="4877645476959324760">"ステータスバー"</string>
+ <string name="overview" msgid="4018602013895926956">"概要"</string>
<string name="demo_mode" msgid="2389163018533514619">"デモモード"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"デモモードを有効にする"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"デモモードを表示"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ステータスバーに時計の秒を表示します。電池使用量に影響する可能性があります。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"クイック設定を並べ替え"</string>
<string name="show_brightness" msgid="6613930842805942519">"クイック設定に明るさ調整バーを表示する"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"高速切り替えを有効にする"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"[概要] ボタンによるページ切り替えを有効にします"</string>
<string name="experimental" msgid="6198182315536726162">"試験運用版"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"BluetoothをONにしますか?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"タブレットでキーボードに接続するには、最初にBluetoothをONにする必要があります。"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 8d754c4..17226e2 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ბატარეის დონის პროცენტის ჩვენება სტატუსის ზოლის ხატულას შიგნით, როდესაც არ იტენება"</string>
<string name="quick_settings" msgid="10042998191725428">"სწრაფი პარამეტრები"</string>
<string name="status_bar" msgid="4877645476959324760">"სტატუსის ზოლი"</string>
+ <string name="overview" msgid="4018602013895926956">"მიმოხილვა"</string>
<string name="demo_mode" msgid="2389163018533514619">"დემო-რეჟიმი"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"დემო-რეჟიმის ჩართვა"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"დემო-რეჟიმის ჩვენება"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"საათის წამების ჩვენება სტატუსის ზოლში. შეიძლება გავლენა იქონიოს ბატარეაზე."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"სწრაფი პარამეტრების გადაწყობა"</string>
<string name="show_brightness" msgid="6613930842805942519">"სიკაშკაშის ჩვენება სწრაფ პარამეტრებში"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"სწრაფი გადართვის ჩართვა"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"მიმოხილვის ღილაკის მეშვეობით გვერდების გადაფურცვლის ჩართვა"</string>
<string name="experimental" msgid="6198182315536726162">"ექსპერიმენტული"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"გსურთ Bluetooth-ის ჩართვა?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"კლავიატურის ტაბლეტთან დასაკავშირებლად, ჯერ უნდა ჩართოთ Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index d0bd8bb..d111891 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Зарядталмай тұрғанда, күй жолағы белгішесінің ішінде батарея деңгейінің пайыздық шамасын көрсетеді"</string>
<string name="quick_settings" msgid="10042998191725428">"Жылдам параметрлер"</string>
<string name="status_bar" msgid="4877645476959324760">"Күйін көрсету жолағы"</string>
+ <string name="overview" msgid="4018602013895926956">"Шолу"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демо режимі"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Демо режимін қосу"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Демо режимін көрсету"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Күйін көрсету жолағында сағат секундтарын көрсету. Батареяның қызмет көрсету мерзіміне әсер етуі мүмкін."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Жылдам параметрлерді қайта реттеу"</string>
<string name="show_brightness" msgid="6613930842805942519">"Жылдам параметрлерде жарықтықты көрсету"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Тез ауыстырып қосуды қосу"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"\"Шолу\" түймесі арқылы беттерді аударуды қосу"</string>
<string name="experimental" msgid="6198182315536726162">"Эксперименттік"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth функциясын қосу керек пе?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Пернетақтаны планшетке қосу үшін алдымен Bluetooth функциясын қосу керек."</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index d77f480..6ed8f8b 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"បង្ហាញភាគរយនៃកម្រិតថាមពលថ្មនៅក្នុងរូបតំណាងរបារស្ថានភាពនៅពេលមិនសាកថ្ម"</string>
<string name="quick_settings" msgid="10042998191725428">"ការកំណត់រហ័ស"</string>
<string name="status_bar" msgid="4877645476959324760">"របារស្ថានភាព"</string>
+ <string name="overview" msgid="4018602013895926956">"ទិដ្ឋភាព"</string>
<string name="demo_mode" msgid="2389163018533514619">"របៀបសាកល្បង"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"បើករបៀបសាកល្បង"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"បង្ហាញរបៀបសាកល្បង"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"បង្ហាញវិនាទីនៅលើរបារស្ថានភាពអាចនឹងប៉ះពាល់ដល់ថាមពលថ្ម។"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"រៀបចំការកំណត់រហ័សឡើងវិញ"</string>
<string name="show_brightness" msgid="6613930842805942519">"បង្ហាញកម្រិតពន្លឺនៅក្នុងការកំណត់រហ័ស"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"បើកដំណើរការបិទ/បើករហ័ស"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"បើកដំណើការចុះទំព័រតាមរយៈប៊ូតុងទិដ្ឋភាព"</string>
<string name="experimental" msgid="6198182315536726162">"ពិសោធន៍"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"បើកប៊្លូធូសឬ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ដើម្បីភ្ជាប់ក្តារចុចរបស់អ្នកជាមួយនឹងថេប្លេតរបស់អ្នក អ្នកត្រូវតែបើកប៊្លូធូសជាមុនសិន។"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 3b9dfc2..c329485 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ಚಾರ್ಜ್ ಮಾಡದಿರುವಾಗ ಸ್ಥಿತಿ ಪಟ್ಟಿ ಐಕಾನ್ ಒಳಗೆ ಬ್ಯಾಟರಿ ಮಟ್ಟದ ಶೇಕಡಾವನ್ನು ತೋರಿಸಿ"</string>
<string name="quick_settings" msgid="10042998191725428">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="status_bar" msgid="4877645476959324760">"ಸ್ಥಿತಿ ಪಟ್ಟಿ"</string>
+ <string name="overview" msgid="4018602013895926956">"ಸಮಗ್ರ ನೋಟ"</string>
<string name="demo_mode" msgid="2389163018533514619">"ಡೆಮೊ ಮೋಡ್"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ಡೆಮೊ ಮೋಡ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ಡೆಮೊ ಮೋಡ್ ತೋರಿಸು"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಲ್ಲಿ ಗಡಿಯಾರ ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು. ಇದಕ್ಕೆ ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯು ಪರಿಣಾಮಬೀರಬಹುದು."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
<string name="show_brightness" msgid="6613930842805942519">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಪ್ರಖರತೆಯನ್ನು ತೋರಿಸಿ"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"ವೇಗವಾಗಿ ಟಾಗಲ್ ಮಾಡುವಿಕೆ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"ಸಮಗ್ರ ನೋಟದ ಬಟನ್ ಮೂಲಕ ಪೇಜಿಂಗ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="experimental" msgid="6198182315536726162">"ಪ್ರಾಯೋಗಿಕ"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡುವುದೇ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್ಗೆ ಸಂಪರ್ಕಿಸಲು, ನೀವು ಮೊದಲು ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index a5f14e4..1a956ec 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"충전 중이 아닌 경우 상태 표시줄 아이콘 내에 배터리 잔량 비율 표시"</string>
<string name="quick_settings" msgid="10042998191725428">"빠른 설정"</string>
<string name="status_bar" msgid="4877645476959324760">"상태 표시줄"</string>
+ <string name="overview" msgid="4018602013895926956">"개요"</string>
<string name="demo_mode" msgid="2389163018533514619">"데모 모드"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"데모 모드 사용"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"데모 모드 표시"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"상태 표시줄에 시계 초 단위를 표시합니다. 배터리 수명에 영향을 줄 수도 있습니다."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"빠른 설정 재정렬"</string>
<string name="show_brightness" msgid="6613930842805942519">"빠른 설정에서 밝기 표시"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"빠른 전환 사용"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"개요 버튼을 통한 페이징 사용"</string>
<string name="experimental" msgid="6198182315536726162">"베타"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"블루투스를 켜시겠습니까?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"키보드를 태블릿에 연결하려면 먼저 블루투스를 켜야 합니다."</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 2d6dc6f..0dbae90 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Түзмөк кубаттанбай турганда, батареянын деңгээли статус тилкесинде көрүнүп турат"</string>
<string name="quick_settings" msgid="10042998191725428">"Ыкчам жөндөөлөр"</string>
<string name="status_bar" msgid="4877645476959324760">"Абал тилкеси"</string>
+ <string name="overview" msgid="4018602013895926956">"Көз жүгүртүү"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демо режими"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Демо режимин иштетүү"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Демо режимин көрсөтүү"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Абал тилкесинен сааттын секунддары көрсөтүлсүн. Батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ыкчам жөндөөлөрдү кайра коюу"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ыкчам жөндөөлөрдөн жарык деңгээлин көрсөтүү"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Тез которгучту иштетүү"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Сереп баскычы менен барактоону иштетүү"</string>
<string name="experimental" msgid="6198182315536726162">"Сынамык"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth күйгүзүлсүнбү?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Баскычтобуңузду планшетиңизге туташтыруу үчүн, адегенде Bluetooth\'ту күйгүзүшүңүз керек."</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index f7e2344..43e7bac 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -28,4 +28,14 @@
<!-- We have only space for one notification on phone landscape layouts. -->
<integer name="keyguard_max_notification_count">1</integer>
+
+ <!-- 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>
+ <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
+
+ <!-- Recents: The relative range of visible tasks from the current scroll position
+ while the stack is not focused. -->
+ <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
+ <item name="recents_layout_unfocused_range_max" format="float" type="integer">1.5</item>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 82a82e6..c9e5448 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ສະແດງເປີເຊັນລະດັບແບັດເຕີຣີຢູ່ດ້ານໃນໄອຄອນແຖບສະຖານະ ເມື່ອບໍ່ສາກຢູ່"</string>
<string name="quick_settings" msgid="10042998191725428">"ການຕັ້ງຄ່າດ່ວນ"</string>
<string name="status_bar" msgid="4877645476959324760">"ແຖບສະຖານະ"</string>
+ <string name="overview" msgid="4018602013895926956">"ພາບຮວມ"</string>
<string name="demo_mode" msgid="2389163018533514619">"ໂໝດສາທິດ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ເປີດໃຊ້ໂໝດສາທິດ"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ສະແດງໂຫມດສາທິດ"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ສະແດງວິນາທີໂມງຢູ່ໃນແຖບສະຖານະ. ອາດຈະມີຜົນກະທົບຕໍ່ອາຍຸແບັດເຕີຣີ."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ຈັດວາງການຕັ້ງຄ່າດ່ວນຄືນໃໝ່"</string>
<string name="show_brightness" msgid="6613930842805942519">"ສະແດງຄວາມແຈ້ງຢູ່ໃນການຕັ້ງຄ່າດ່ວນ"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"ເປີດນຳໃຊ້ການສັບປ່ຽນໄວ"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"ເປີດນຳໃຊ້ການແບ່ງໜ້າຜ່ານປຸ່ມພາບຮວມ"</string>
<string name="experimental" msgid="6198182315536726162">"ຍັງຢູ່ໃນການທົດລອງ"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ເປີດໃຊ້ Bluetooth ບໍ່?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ເພື່ອເຊື່ອມຕໍ່ແປ້ນພິມຂອງທ່ານກັບແທັບເລັດຂອງທ່ານ, ກ່ອນອື່ນໝົດທ່ານຕ້ອງເປີດ Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 2341e5a..d5292aa 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Rodyti akumuliatoriaus įkrovos lygio procentinę vertę būsenos juostos piktogramoje, kai įrenginys nėra įkraunamas"</string>
<string name="quick_settings" msgid="10042998191725428">"Spartieji nustatymai"</string>
<string name="status_bar" msgid="4877645476959324760">"Būsenos juosta"</string>
+ <string name="overview" msgid="4018602013895926956">"Apžvalga"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demonstracinis režimas"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Įgalinti demonstracinį režimą"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Rodyti demonstraciniu režimu"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Rodyti laikrodžio sekundes būsenos juostoje. Tai gali paveikti akumuliatoriaus naudojimo laiką."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Pertvarkyti sparčiuosius nustatymus"</string>
<string name="show_brightness" msgid="6613930842805942519">"Rodyti skaistį sparčiuosiuose nustatymuose"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Įgalinti greitą perjungimą"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Įgalinti puslapių kaitą per apžvalgos mygtuką"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentinė versija"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Įjungti „Bluetooth“?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Kad galėtumėte prijungti klaviatūrą prie planšetinio kompiuterio, pirmiausia turite įjungti „Bluetooth“."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 27663bd..34643b0 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -410,6 +410,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Rādīt akumulatora uzlādes līmeni procentos statusa joslas ikonā, kad netiek veikta uzlāde"</string>
<string name="quick_settings" msgid="10042998191725428">"Ātrie iestatījumi"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusa josla"</string>
+ <string name="overview" msgid="4018602013895926956">"Pārskats"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demonstrācijas režīms"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Iespējot demonstrācijas režīmu"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Rādīt demonstrācijas režīmu"</string>
@@ -438,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Statusa joslā rādīt pulksteņa sekundes. Var ietekmēt akumulatora darbības laiku."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Pārkārtot ātros iestatījumus"</string>
<string name="show_brightness" msgid="6613930842805942519">"Rādīt spilgtumu ātrajos iestatījumos"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Iespējot ātro pārslēgšanu"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Iespējot lapošanu, izmantojot pogu Pārskats"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentāli"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vai ieslēgt Bluetooth savienojumu?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Lai pievienotu tastatūru planšetdatoram, vispirms ir jāieslēdz Bluetooth savienojums."</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 7162a5c..310b192 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Прикажи процент на ниво на батеријата во внатрешноста на иконата со статусна лента кога не се полни"</string>
<string name="quick_settings" msgid="10042998191725428">"Брзи поставки"</string>
<string name="status_bar" msgid="4877645476959324760">"Статусна лента"</string>
+ <string name="overview" msgid="4018602013895926956">"Краток преглед"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демо-режим"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Овозможи демо-режим"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Прикажи демо-режим"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Прикажи ги секундите на часовникот на статусната лента. Може да влијае на траењето на батеријата."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Преуредете ги Брзи поставки"</string>
<string name="show_brightness" msgid="6613930842805942519">"Прикажете ја осветленоста во Брзи поставки"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Овозможете брзо префрлање"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Овозможете прелистување преку копчето Краток преглед"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Да се вклучи Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"За да ја поврзете тастатурата со таблетот, најпрво треба да вклучите Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 5df98ec..bdad812 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ചാർജ്ജുചെയ്യാതിരിക്കുമ്പോൾ സ്റ്റാറ്റസ് ബാർ ഐക്കണിൽ ബാറ്ററി ലെവൽ ശതമാനം കാണിക്കുക"</string>
<string name="quick_settings" msgid="10042998191725428">"ദ്രുത ക്രമീകരണം"</string>
<string name="status_bar" msgid="4877645476959324760">"സ്റ്റാറ്റസ് ബാർ"</string>
+ <string name="overview" msgid="4018602013895926956">"ചുരുക്കവിവരണം"</string>
<string name="demo_mode" msgid="2389163018533514619">"ഡെമോ മോഡ്"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ഡെമോ മോഡ് പ്രവർത്തനക്ഷമമാക്കുക"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ഡെമോ മോഡ് കാണിക്കുക"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"സ്റ്റാറ്റസ് ബാറിൽ ക്ലോക്ക് സെക്കൻഡ് കാണിക്കുന്നത് ബാറ്ററിയുടെ ലൈഫിനെ ബാധിക്കാം."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ദ്രുത ക്രമീകരണം പുനഃസജ്ജീകരിക്കുക"</string>
<string name="show_brightness" msgid="6613930842805942519">"ദ്രുത ക്രമീകരണത്തിൽ തെളിച്ചം കാണിക്കുക"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"അതിവേഗ ടോഗിൾ പ്രവർത്തനക്ഷമമാക്കുക"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"ചുരുക്കവിവരണ ബട്ടൺ വഴി പേജിംഗ് പ്രവർത്തനക്ഷമമാക്കുക"</string>
<string name="experimental" msgid="6198182315536726162">"പരീക്ഷണാത്മകം!"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ഓണാക്കണോ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"നിങ്ങളുടെ ടാബ്ലെറ്റുമായി കീബോർഡ് കണക്റ്റുചെയ്യുന്നതിന്, ആദ്യം Bluetooth ഓണാക്കേണ്ടതുണ്ട്."</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index f960dfa..f97be1ef 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -407,6 +407,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Тэжээлийн хувийг цэнэглээгүй байх үед статусын хэсэгт харуулна уу"</string>
<string name="quick_settings" msgid="10042998191725428">"Түргэвчилсэн Tохиргоо"</string>
<string name="status_bar" msgid="4877645476959324760">"Статус самбар"</string>
+ <string name="overview" msgid="4018602013895926956">"Тойм"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демо горим"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Демо горимыг идэвхжүүлэх"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Демо горимыг харуулах"</string>
@@ -435,6 +436,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Статус талбарт цагийн секундыг харуулах. Энэ нь тэжээлийн цэнэгт нөлөөлж болно."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Түргэн тохиргоог дахин засварлах"</string>
<string name="show_brightness" msgid="6613930842805942519">"Түргэн тохиргоонд гэрэлтүүлэг харах"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Түргэн унтраах/асаахыг идэвхжүүлэх"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Тойм товчлуурыг ашиглан хуудас дугаарлахыг идэвхжүүлэх"</string>
<string name="experimental" msgid="6198182315536726162">"Туршилтын"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth-г асаах уу?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Компьютерийн гараа таблетад холбохын тулд эхлээд Bluetooth-г асаана уу."</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index f599aca..f9ff472 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"चार्ज होत नसताना स्टेटस बार चिन्हामध्ये बॅटरी पातळी टक्केवारी दर्शवा"</string>
<string name="quick_settings" msgid="10042998191725428">"दृत सेटिंग्ज"</string>
<string name="status_bar" msgid="4877645476959324760">"स्टेटस बार"</string>
+ <string name="overview" msgid="4018602013895926956">"विहंगावलोकन"</string>
<string name="demo_mode" msgid="2389163018533514619">"डेमो मोड"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"डेमो मोड सक्षम करा"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"डेमो मोड दर्शवा"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"स्टेटस बारमध्ये घड्याळ सेकंद दर्शवा. कदाचित बॅटरी आयुष्य प्रभावित होऊ शकते."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिंग्जची पुनर्रचना करा"</string>
<string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिंग्जमध्ये चमक दर्शवा"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"जलद टॉगल करा सक्षम करा"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"विहंगावलोकन बटणाद्वारे लिखाण सक्षम करा"</string>
<string name="experimental" msgid="6198182315536726162">"प्रायोगिक"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटुथ सुरू करायचे?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"आपला कीबोर्ड आपल्या टॅब्लेटसह कनेक्ट करण्यासाठी, आपल्याला प्रथम ब्लूटुथ चालू करणे आवश्यक आहे."</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 6511d30..254af9b 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Tunjukkan peratusan aras bateri dalam ikon bar status semasa tidak mengecas"</string>
<string name="quick_settings" msgid="10042998191725428">"Tetapan Pantas"</string>
<string name="status_bar" msgid="4877645476959324760">"Bar status"</string>
+ <string name="overview" msgid="4018602013895926956">"Ikhtisar"</string>
<string name="demo_mode" msgid="2389163018533514619">"Mod tunjuk cara"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Dayakan mod tunjuk cara"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Tunjukkan mod tunjuk cara"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Tunjukkan saat jam dalam bar status. Mungkin menjejaskan hayat bateri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Susun Semula Tetapan Pantas"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tunjukkan kecerahan dalam Tetapan Pantas"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Mendayakan togol pantas"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Mendayakan penghalaman melalui butang Ikhtisar"</string>
<string name="experimental" msgid="6198182315536726162">"Percubaan"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Hidupkan Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Untuk menyambungkan papan kekunci anda dengan tablet, anda perlu menghidupkan Bluetooth terlebih dahulu."</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index c8551b7..9732fc4 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"အားမသွင်းနေစဉ်တွင် ဘတ်ထရီအဆင့် ရာခိုင်နှုန်းကို အခြေနေပြဘား အိုင်ကွန်တွင် ပြပါ"</string>
<string name="quick_settings" msgid="10042998191725428">"အမြန် ဆက်တင်များ"</string>
<string name="status_bar" msgid="4877645476959324760">"အခြေအနေပြနေရာ"</string>
+ <string name="overview" msgid="4018602013895926956">"ခြုံငုံသုံးသပ်ချက်"</string>
<string name="demo_mode" msgid="2389163018533514619">"သရုပ်ပြ မုဒ်"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"သရုပ်ပြမုဒ်ကို ဖွင့်ရန်"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"သရုပ်ပြမုဒ် ပြရန်"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"အခြေအနေပြနေရာမှာ နာရီ စက္ကန့်များကို ပြပါ။ ဘက်ထရီ သက်တမ်းကို အကျိုးသက်ရောက်နိုင်တယ်။"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"အမြန် ဆက်တင်များကို ပြန်စီစဉ်ရန်"</string>
<string name="show_brightness" msgid="6613930842805942519">"အမြန် ဆက်တင်များထဲက တောက်ပမှုကို ပြရန်"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"အမြန်ဖွင့်/ပိတ်ခြင်း ဖွင့်ရန်"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"ခြုံငုံသုံးသပ်ချက်ခလုတ်မှတစ်ဆင့် စာမျက်နှာခွဲခြင်းကို ဖွင့်ပါ"</string>
<string name="experimental" msgid="6198182315536726162">"စမ်းသပ်ရေး"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ဘလူးတုသ် ဖွင့်ရမလား။"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ကီးဘုတ်ကို တပ်ဘလက်နှင့် ချိတ်ဆက်ရန်၊ ပမထဦးစွာ ဘလူးတုသ်ကို ဖွင့်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7cfcbe2..6bdc8e3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Vis batterinivåprosenten inni statusfeltikonet når du ikke lader"</string>
<string name="quick_settings" msgid="10042998191725428">"Hurtiginnstillinger"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusrad"</string>
+ <string name="overview" msgid="4018602013895926956">"Oversikt"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo-modus"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Slå på demo-modus"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Vis demo-modus"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Vis sekunder i statusfeltet på klokken. Det kan påvirke batteritiden."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Omorganiser hurtiginnstillingene"</string>
<string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i hurtiginnstillingene"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Slå på hurtigveksling"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Slå på paginering via Oversikt-knappen"</string>
<string name="experimental" msgid="6198182315536726162">"På forsøksstadiet"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vil du slå på Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"For å koble tastaturet til nettbrettet ditt må du først slå på Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 078f783..72397bb 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"चार्ज नगरेको बेला वस्तुस्थिति पट्टी आइकन भित्र ब्याट्री प्रतिशत स्तर देखाउनुहोस्"</string>
<string name="quick_settings" msgid="10042998191725428">"द्रुत सेटिङहरू"</string>
<string name="status_bar" msgid="4877645476959324760">"स्थिति पट्टी"</string>
+ <string name="overview" msgid="4018602013895926956">"परिदृश्य"</string>
<string name="demo_mode" msgid="2389163018533514619">"डेमो मोड"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"डेमो मोड सक्षम गर्नुहोस्"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"डेमो मोड देखाउनुहोस्"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"वस्तुस्थिति पट्टीको घडीमा सेकेन्ड देखाउनुहोस्। ब्याट्री आयु प्रभावित हुन सक्छ।"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिङहरू पुनः व्यवस्थित गर्नुहोस्"</string>
<string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिङहरूमा उज्यालो देखाउनुहोस्"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"छिटो टगल सक्रिय गर्नुहोस्"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"परिदृश्य बटन मार्फत पेजिङ सक्रिय गर्नुहोस्"</string>
<string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लुटुथ सक्रिय पार्ने हो?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index de4e58a..d7dce73 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Accupercentage weergeven in het pictogram op de statusbalk wanneer er niet wordt opgeladen"</string>
<string name="quick_settings" msgid="10042998191725428">"Snelle instellingen"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusbalk"</string>
+ <string name="overview" msgid="4018602013895926956">"Overzicht"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demomodus"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demomodus inschakelen"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demomodus weergeven"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Klokseconden op de statusbalk weergeven. Kan van invloed zijn op de accuduur."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Snelle instellingen opnieuw indelen"</string>
<string name="show_brightness" msgid="6613930842805942519">"Helderheid weergeven in Snelle instellingen"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Snel schakelen inschakelen"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Paginering via de knop Overzicht inschakelen"</string>
<string name="experimental" msgid="6198182315536726162">"Experimenteel"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth inschakelen?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Als je je toetsenbord wilt verbinden met je tablet, moet je eerst Bluetooth inschakelen."</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index e5cbf1f..69ebcba 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ਜਦੋਂ ਚਾਰਜ ਨਾ ਹੋ ਰਹੀ ਹੋਵੇ ਤਾਂ ਸਥਿਤੀ ਬਾਰ ਦੇ ਅੰਦਰ ਬੈਟਰੀ ਪੱਧਰ ਪ੍ਰਤਿਸ਼ਤਤਾ ਦਿਖਾਓ"</string>
<string name="quick_settings" msgid="10042998191725428">"ਤਤਕਾਲ ਸੈੱਟਿੰਗਜ਼"</string>
<string name="status_bar" msgid="4877645476959324760">"ਸਥਿਤੀ ਬਾਰ"</string>
+ <string name="overview" msgid="4018602013895926956">"ਰੂਪ-ਰੇਖਾ"</string>
<string name="demo_mode" msgid="2389163018533514619">"ਡੈਮੋ ਮੋਡ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ਡੈਮੋ ਮੋਡ ਸਮਰੱਥ ਬਣਾਓ"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ਡੈਮੋ ਮੋਡ ਦੇਖੋ"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"ਸਥਿਤੀ ਬਾਰ ਵਿੱਚ ਘੜੀ ਸਕਿੰਟ ਦਿਖਾਓ। ਬੈਟਰੀ ਸਮਰੱਥਾ ਤੇ ਅਸਰ ਪੈ ਸਕਦਾ ਹੈ।"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਦੁਬਾਰਾ ਕ੍ਰਮ ਦਿਓ"</string>
<string name="show_brightness" msgid="6613930842805942519">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਮਕ ਦਿਖਾਓ"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"ਤੇਜ਼ ਬਦਲੋ ਨੂੰ ਯੋਗ ਬਣਾਓ"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"ਰੂਪ-ਰੇਖਾ ਬਟਨ ਦੁਆਰਾ ਪੇਜਿੰਗ ਨੂੰ ਯੋਗ ਬਣਾਓ"</string>
<string name="experimental" msgid="6198182315536726162">"ਪ੍ਰਯੋਗਾਤਮਿਕ"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth ਚਾਲੂ ਕਰੋ?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ਆਪਣੇ ਟੈਬਲੇਟ ਨਾਲ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ Bluetooth ਚਾਲੂ ਕਰਨ ਦੀ ਜ਼ਰੂਰਤ ਹੈ।"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 65d19bb..f777255 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Pokaż procent naładowania baterii w ikonie na pasku stanu, gdy telefon się nie ładuje"</string>
<string name="quick_settings" msgid="10042998191725428">"Szybkie ustawienia"</string>
<string name="status_bar" msgid="4877645476959324760">"Pasek stanu"</string>
+ <string name="overview" msgid="4018602013895926956">"Przegląd"</string>
<string name="demo_mode" msgid="2389163018533514619">"Tryb demonstracyjny"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Włącz tryb demonstracyjny"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Pokaż tryb demonstracyjny"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Pokaż sekundy na zegarku na pasku stanu. Może mieć wpływ na czas pracy baterii."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Uporządkuj Szybkie ustawienia"</string>
<string name="show_brightness" msgid="6613930842805942519">"Pokaż jasność w Szybkich ustawieniach"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Włącz szybkie przełączanie"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Włącz stronicowanie za pomocą przycisku Przegląd"</string>
<string name="experimental" msgid="6198182315536726162">"Eksperymentalne"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Włączyć Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Aby połączyć klawiaturę z tabletem, musisz najpierw włączyć Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8448717..2192bda 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentagem de nível de bateria dentro do ícone da barra de status quando não estiver carregando"</string>
<string name="quick_settings" msgid="10042998191725428">"Configurações rápidas"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de status"</string>
+ <string name="overview" msgid="4018602013895926956">"Visão geral"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo de demonstração"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ativar modo de demonstração"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demonstração"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Ativar alternância rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Ativar paginação pelo botão \"Visão geral\""</string>
<string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Ativar o Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6abe9a9..ebdd653 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar a percentagem do nível da bateria no ícone da barra de estado quando não estiver a carregar"</string>
<string name="quick_settings" msgid="10042998191725428">"Definições rápidas"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de estado"</string>
+ <string name="overview" msgid="4018602013895926956">"Vista geral"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo de demonstração"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ativar o modo de demonstração"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demonstração"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de estado. Pode afetar a autonomia da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar as Definições rápidas"</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar luminosidade nas Definições rápidas"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Ativar alternância rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Ativar a paginação através do botão Vista geral"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Pretende ativar o Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para ligar o teclado ao tablet, tem de ativar primeiro o Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8448717..2192bda 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Mostrar porcentagem de nível de bateria dentro do ícone da barra de status quando não estiver carregando"</string>
<string name="quick_settings" msgid="10042998191725428">"Configurações rápidas"</string>
<string name="status_bar" msgid="4877645476959324760">"Barra de status"</string>
+ <string name="overview" msgid="4018602013895926956">"Visão geral"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modo de demonstração"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Ativar modo de demonstração"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Mostrar modo de demonstração"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostrar segundos do relógio na barra de status. Pode afetar a duração da bateria."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
<string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Ativar alternância rápida"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Ativar paginação pelo botão \"Visão geral\""</string>
<string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Ativar o Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index b45347f..c749ae1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -410,6 +410,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Afișați procentajul cu nivelul bateriei în interiorul pictogramei din bara de stare, atunci când nu se încarcă"</string>
<string name="quick_settings" msgid="10042998191725428">"Setări rapide"</string>
<string name="status_bar" msgid="4877645476959324760">"Bară de stare"</string>
+ <string name="overview" msgid="4018602013895926956">"Recente"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modul demonstrativ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Activați modul demonstrativ"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Afișați modul demonstrativ"</string>
@@ -438,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Afișează secundele pe ceas în bara de stare. Poate afecta autonomia bateriei."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Rearanjați Setările rapide"</string>
<string name="show_brightness" msgid="6613930842805942519">"Afișați luminozitatea în Setările rapide"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Activați comutarea rapidă"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Activați paginarea prin butonul Recente"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentale"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Activați Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 96971ea..0de29f0 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Когда устройство работает в автономном режиме, процент заряда батареи показан в строке состояния"</string>
<string name="quick_settings" msgid="10042998191725428">"Быстрые настройки"</string>
<string name="status_bar" msgid="4877645476959324760">"Строка состояния"</string>
+ <string name="overview" msgid="4018602013895926956">"Обзор"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демонстрация"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Включить демонстрационный режим"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Перейти в демонстрационный режим"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показывать в строке состояния время с точностью до секунды (заряд батареи может расходоваться быстрее)."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Изменить порядок Быстрых настроек"</string>
<string name="show_brightness" msgid="6613930842805942519">"Добавить яркость в Быстрые настройки"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Быстрая разбивка на страницы"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Разбивать список недавних приложений на страницы с помощью кнопки \"Обзор\"."</string>
<string name="experimental" msgid="6198182315536726162">"Экспериментальная функция"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Подключение по Bluetooth"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Чтобы подключить клавиатуру к планшету, включите Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 64d460e..07c9197 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ආරෝපණය නොවන විට තත්ත්ව තීරු නිරූපකය ඇතුළත බැටරි මට්ටම් ප්රතිශතය පෙන්වන්න"</string>
<string name="quick_settings" msgid="10042998191725428">"ඉක්මන් සැකසීම්"</string>
<string name="status_bar" msgid="4877645476959324760">"තත්ත්ව තීරුව"</string>
+ <string name="overview" msgid="4018602013895926956">"දළ විශ්ලේෂණය"</string>
<string name="demo_mode" msgid="2389163018533514619">"ආදර්ශන ප්රකාරය"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ආදර්ශන ප්රකාරය සබල කරන්න"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ආදර්ශන ප්රකාරය පෙන්වන්න"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"තත්ත්ව තීරුවෙහි ඔරලෝසු තත්පර පෙන්වන්න. බැටරි ආයු කාලයට බලපෑමට හැකිය."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"ඉක්මන් සැකසීම් යළි පිළිවෙළට සකසන්න"</string>
<string name="show_brightness" msgid="6613930842805942519">"ඉක්මන් සැකසීම්වල දීප්තිය පෙන්වන්න"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"වේගවත් ටොගල කිරීම සබල කරන්න"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"දළ විශ්ලේෂණ බොත්තම හරහා පේජින් සබල කරන්න"</string>
<string name="experimental" msgid="6198182315536726162">"පරීක්ෂණාත්මක"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"බ්ලූටූත් ක්රියාත්මක කරන්නද?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"ඔබේ යතුරු පුවරුව ඔබේ ටැබ්ලට් පරිගණකයට සම්බන්ධ කිරීමට, ඔබ පළමුව බ්ලූටූත් ක්රියාත්මක කළ යුතුය."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index def49e9..fe3c26b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Percentuálne zobrazenie nabitia batérie vnútri ikony v stavovom riadku, keď neprebieha nabíjanie"</string>
<string name="quick_settings" msgid="10042998191725428">"Rýchle nastavenia"</string>
<string name="status_bar" msgid="4877645476959324760">"Stavový riadok"</string>
+ <string name="overview" msgid="4018602013895926956">"Prehľad"</string>
<string name="demo_mode" msgid="2389163018533514619">"Režim ukážky"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Povoliť režim ukážky"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Zobraziť režim ukážky"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Zobrazí sekundy v stavovom riadku. Môže to ovplyvňovať výdrž batérie."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Zmeniť usporiadanie Rýchlych nastavení"</string>
<string name="show_brightness" msgid="6613930842805942519">"Zobraziť jas v Rýchlych nastaveniach"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Povoliť rýchle prepínanie"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Povoľte prechádzanie po stranách prostredníctvom tlačidla Prehľad"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentálne"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Zapnúť Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Ak chcete klávesnicu pripojiť k tabletu, najprv musíte zapnúť Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 14d3bdb..6b32443 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikaz odstotka napolnjenosti akumulatorja znotraj ikone v vrstici stanja, ko se ne polni"</string>
<string name="quick_settings" msgid="10042998191725428">"Hitre nastavitve"</string>
<string name="status_bar" msgid="4877645476959324760">"Vrstica stanja"</string>
+ <string name="overview" msgid="4018602013895926956">"Pregled"</string>
<string name="demo_mode" msgid="2389163018533514619">"Predstavitveni način"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Omogočanje predstavitvenega načina"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Prikaz predstavitvenega načina"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Prikaže sekunde pri uri v vrstici stanja. To lahko vpliva na čas delovanja pri akumulatorskem napajanju."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Preuredi hitre nastavitve"</string>
<string name="show_brightness" msgid="6613930842805942519">"Prikaz svetlosti v hitrih nastavitvah"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Omogoči hiter preklop"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Omogoči pregled strani z gumbom za pregled"</string>
<string name="experimental" msgid="6198182315536726162">"Poskusno"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Želite vklopiti Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Če želite povezati tipkovnico in tablični računalnik, vklopite Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 0316c24..255fb09 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Shfaq përqindjen e nivelit të baterisë brenda ikonës së shiritit të statusit kur nuk është duke u ngarkuar."</string>
<string name="quick_settings" msgid="10042998191725428">"Cilësimet e shpejta"</string>
<string name="status_bar" msgid="4877645476959324760">"Shiriti i statusit"</string>
+ <string name="overview" msgid="4018602013895926956">"Përmbledhje"</string>
<string name="demo_mode" msgid="2389163018533514619">"Modaliteti i demonstrimit"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Aktivizo modalitetin e demonstrimit"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Shfaq modalitetin e demonstrimit"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te jeta e baterisë."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Risistemo Cilësimet e shpejta"</string>
<string name="show_brightness" msgid="6613930842805942519">"Shfaq ndriçimin te Cilësimet e shpejta"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Aktivizo ndërrimin e shpejtë"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Aktivizo shfletimin përmes butonit \"Përmbledhje\""</string>
<string name="experimental" msgid="6198182315536726162">"Eksperimentale"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Të aktivizohet \"bluetooth-i\"?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Për të lidhur tastierën me tabletin, në fillim duhet të aktivizosh \"bluetooth-in\"."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2139441..25232c0 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -410,6 +410,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Приказивање нивоа напуњености батерије у процентима унутар иконе на статусној траци када се батерија не пуни"</string>
<string name="quick_settings" msgid="10042998191725428">"Брза подешавања"</string>
<string name="status_bar" msgid="4877645476959324760">"Статусна трака"</string>
+ <string name="overview" msgid="4018602013895926956">"Преглед"</string>
<string name="demo_mode" msgid="2389163018533514619">"Режим демонстрације"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Омогући режим демонстрације"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Прикажи режим демонстрације"</string>
@@ -438,6 +439,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Секунде на сату се приказују на статусној траци. То може да утиче на трајање батерије."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Преуреди Брза подешавања"</string>
<string name="show_brightness" msgid="6613930842805942519">"Прикажи осветљеност у Брзим подешавањима"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Омогући брзо листање"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Омогућава листање помоћу дугмета Преглед"</string>
<string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Желите ли да укључите Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Да бисте повезали тастатуру са таблетом, прво морате да укључите Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index ad7bffd..035b2e7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Visa batterinivå i procent i statusfältsikonen när enheten inte laddas"</string>
<string name="quick_settings" msgid="10042998191725428">"Snabbinställningar"</string>
<string name="status_bar" msgid="4877645476959324760">"Statusfält"</string>
+ <string name="overview" msgid="4018602013895926956">"Översikt"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demoläge"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Aktivera demoläge"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Visa demoläge"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Visa klocksekunder i statusfältet. Detta kan påverka batteritiden."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ordna snabbinställningarna"</string>
<string name="show_brightness" msgid="6613930842805942519">"Visa ljusstyrka i snabbinställningarna"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Aktivera snabb aktivering och inaktivering"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Aktivera sidindelning via knappen Översikt"</string>
<string name="experimental" msgid="6198182315536726162">"Experimentella"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vill du aktivera Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Om du vill ansluta tangentbordet till surfplattan måste du först aktivera Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 244e58d..7b93044 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Onyesha asilimia ya kiwango cha betri ndani ya aikoni ya sehemu ya arifa inapokuwa haichaji"</string>
<string name="quick_settings" msgid="10042998191725428">"Mipangilio ya Haraka"</string>
<string name="status_bar" msgid="4877645476959324760">"Sehemu ya kuonyesha hali"</string>
+ <string name="overview" msgid="4018602013895926956">"Muhtasari"</string>
<string name="demo_mode" msgid="2389163018533514619">"Hali ya onyesho"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Washa hali ya onyesho"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Onyesha hali ya onyesho"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Onyesha sekunde za saa katika sehemu ya arifa. Inaweza kuathiri muda wa matumizi ya betri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Panga Upya Mipangilio ya Haraka"</string>
<string name="show_brightness" msgid="6613930842805942519">"Onyesha unga\'avu katika Mipangilio ya Haraka"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Washa kugeuza haraka"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Washa nambari za ukurasa kupitia kitufe cha Muhtasari"</string>
<string name="experimental" msgid="6198182315536726162">"Ya majaribio"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Je, ungependa kuwasha Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Ili uunganishe Kibodi yako kwenye kompyuta yako kibao, lazima kwanza uwashe Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 4f6d209..6795da4 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,4 +32,14 @@
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">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">-4</item>
+ <item name="recents_layout_focused_range_max" format="float" type="integer">3</item>
+
+ <!-- Recents: The relative range of visible tasks from the current scroll position
+ while the stack is not focused. -->
+ <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
+ <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 9bd280d..880605c 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"சார்ஜ் செய்யாத போது, நிலைப் பட்டி ஐகானின் உள்ளே பேட்டரி அளவு சதவீதத்தைக் காட்டும்"</string>
<string name="quick_settings" msgid="10042998191725428">"உடனடி அமைப்புகள்"</string>
<string name="status_bar" msgid="4877645476959324760">"நிலைப் பட்டி"</string>
+ <string name="overview" msgid="4018602013895926956">"மேலோட்டப் பார்வை"</string>
<string name="demo_mode" msgid="2389163018533514619">"டெமோ முறை"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"டெமோ முறையை இயக்கு"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"டெமோ முறையைக் காட்டு"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"நிலைப் பட்டியில் கடிகார வினாடிகளைக் காட்டும். பேட்டரியின் ஆயுளைக் குறைக்கலாம்."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"விரைவு அமைப்புகளை மறுவரிசைப்படுத்து"</string>
<string name="show_brightness" msgid="6613930842805942519">"விரைவு அமைப்புகளில் ஒளிர்வுப் பட்டியைக் காட்டு"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"வேகமாக மாறுவதை இயக்கு"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"மேலோட்டப் பார்வை பொத்தான் வழியாக பேஜிங்கை இயக்கும்"</string>
<string name="experimental" msgid="6198182315536726162">"சோதனை முயற்சி"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"புளூடூத்தை இயக்கவா?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"உங்கள் டேப்லெட்டுடன் விசைப்பலகையை இணைக்க, முதலில் புளூடூத்தை இயக்க வேண்டும்."</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 6e16a95..4b96106 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"ఛార్జింగ్లో లేనప్పుడు స్థితి పట్టీ చిహ్నం లోపల బ్యాటరీ స్థాయి శాతం చూపుతుంది"</string>
<string name="quick_settings" msgid="10042998191725428">"శీఘ్ర సెట్టింగ్లు"</string>
<string name="status_bar" msgid="4877645476959324760">"స్థితి పట్టీ"</string>
+ <string name="overview" msgid="4018602013895926956">"స్థూలదృష్టి"</string>
<string name="demo_mode" msgid="2389163018533514619">"డెమో మోడ్"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"డెమో మోడ్ ప్రారంభించండి"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"డెమో మోడ్ చూపు"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"స్థితి పట్టీలో గడియారం సెకన్లు చూపుతుంది. బ్యాటరీ శక్తి ప్రభావితం చేయవచ్చు."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"శీఘ్ర సెట్టింగ్ల ఏర్పాటు క్రమం మార్చు"</string>
<string name="show_brightness" msgid="6613930842805942519">"శీఘ్ర సెట్టింగ్ల్లో ప్రకాశం చూపు"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"వేగవంతమైన టోగుల్ను ప్రారంభించు"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"స్థూలదృష్టి బటన్ ద్వారా పేజింగ్ను ప్రారంభిస్తుంది"</string>
<string name="experimental" msgid="6198182315536726162">"ప్రయోగాత్మకం"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"బ్లూటూత్ ఆన్ చేయాలా?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"మీ కీబోర్డ్ను మీ టాబ్లెట్తో కనెక్ట్ చేయడానికి, మీరు ముందుగా బ్లూటూత్ ఆన్ చేయాలి."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 256b497..3681246 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"แสดงเปอร์เซ็นต์ของระดับแบตเตอรี่ภายในไอคอนแถบสถานะเมื่อไม่มีการชาร์จ"</string>
<string name="quick_settings" msgid="10042998191725428">"การตั้งค่าด่วน"</string>
<string name="status_bar" msgid="4877645476959324760">"แถบสถานะ"</string>
+ <string name="overview" msgid="4018602013895926956">"ภาพรวม"</string>
<string name="demo_mode" msgid="2389163018533514619">"โหมดสาธิต"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"เปิดใช้โหมดสาธิต"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"แสดงโหมดสาธิต"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"แสดงวินาทีของนาฬิกาในแถบสถานะ อาจส่งผลต่ออายุแบตเตอรี"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"จัดเรียงการตั้งค่าด่วนใหม่"</string>
<string name="show_brightness" msgid="6613930842805942519">"แสดงความสว่างในการตั้งค่าด่วน"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"เปิดใช้การสลับแบบด่วน"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"เปิดใช้การสลับหน้าผ่านทางปุ่มภาพรวม"</string>
<string name="experimental" msgid="6198182315536726162">"ทดสอบ"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"เปิดบลูทูธไหม"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"หากต้องการเชื่อมต่อแป้นพิมพ์กับแท็บเล็ต คุณต้องเปิดบลูทูธก่อน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 66b0b6b..e2f8af0 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Ipakita ang porsyento ng antas ng baterya na nasa icon ng status bar kapag nagcha-charge"</string>
<string name="quick_settings" msgid="10042998191725428">"Mga Maikling Setting"</string>
<string name="status_bar" msgid="4877645476959324760">"Status bar"</string>
+ <string name="overview" msgid="4018602013895926956">"Pangkalahatang-ideya"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo mode"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"I-enable ang demo mode"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Ipakita ang demo mode"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Ipakita ang mga segundo ng orasan sa status bar. Maaaring makaapekto sa tagal ng baterya."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Ayusing Muli ang Mga Mabilisang Setting"</string>
<string name="show_brightness" msgid="6613930842805942519">"Ipakita ang liwanag sa Mga Mabilisang Setting"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"I-enable ang mabilis na pag-toggle"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"I-enable ang paging sa pamamagitan ng button na Pangkalahatang-ideya"</string>
<string name="experimental" msgid="6198182315536726162">"Pang-eksperimento"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"I-on ang Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Upang ikonekta ang iyong keyboard sa iyong tablet, kailangan mo munang i-on ang Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 5a6e5db..68cab35 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Şarj olmazken durum çubuğu simgesinin içinde pil düzeyi yüzdesini göster"</string>
<string name="quick_settings" msgid="10042998191725428">"Hızlı Ayarlar"</string>
<string name="status_bar" msgid="4877645476959324760">"Durum çubuğu"</string>
+ <string name="overview" msgid="4018602013895926956">"Genel Bakış"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo modu"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demo modunu etkinleştir"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demo modunu göster"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Durum çubuğunda saatin saniyelerini gösterir. Pil ömrünü etkileyebilir."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Hızlı Ayarlar\'ı Yeniden Düzenle"</string>
<string name="show_brightness" msgid="6613930842805942519">"Hızlı Ayarlar\'da parlaklığı göster"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Hızlı açma/kapatmayı etkinleştir"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Genel bakış düğmesiyle sayfalara ayırmayı etkinleştirin"</string>
<string name="experimental" msgid="6198182315536726162">"Deneysel"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth açılsın mı?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Klavyenizi tabletinize bağlamak için önce Bluetooth\'u açmanız gerekir."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index afa9077..e76fa6c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -411,6 +411,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Показувати заряд акумулятора у відсотках в рядку стану, коли пристрій не заряджається"</string>
<string name="quick_settings" msgid="10042998191725428">"Швидкі налаштування"</string>
<string name="status_bar" msgid="4877645476959324760">"Рядок стану"</string>
+ <string name="overview" msgid="4018602013895926956">"Огляд"</string>
<string name="demo_mode" msgid="2389163018533514619">"Демонстраційний режим"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Увімкнути демонстраційний режим"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Показати демонстраційний режим"</string>
@@ -439,6 +440,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Показувати секунди на годиннику в рядку стану. Акумулятор може розряджатися швидше."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Упорядкувати швидкі налаштування"</string>
<string name="show_brightness" msgid="6613930842805942519">"Показувати панель яскравості у швидких налаштуваннях"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Увімкнути швидкий перехід"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Увімкнути перехід між сторінками за допомогою кнопки \"Огляд\""</string>
<string name="experimental" msgid="6198182315536726162">"Експериментальні налаштування"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Увімкнути Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Щоб під’єднати клавіатуру до планшета, спершу потрібно ввімкнути Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 75160cd9..63ce6c9 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"جب چارج نہ ہو رہا ہو تو بیٹری کی سطح کی فیصد اسٹیٹس بار آئیکن کے اندر دکھائیں"</string>
<string name="quick_settings" msgid="10042998191725428">"فوری ترتیبات"</string>
<string name="status_bar" msgid="4877645476959324760">"اسٹیٹس بار"</string>
+ <string name="overview" msgid="4018602013895926956">"مجموعی جائزہ"</string>
<string name="demo_mode" msgid="2389163018533514619">"ڈیمو موڈ"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"ڈیمو موڈ فعال کریں"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"ڈیمو موڈ دکھائیں"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"گھڑی کے سیکنڈز اسٹیٹس بار میں دکھائیں۔ اس کا بیٹری کی زندگی پر اثر پڑ سکتا ہے۔"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"فوری ترتیبات کو دوبارہ ترتیب دیں"</string>
<string name="show_brightness" msgid="6613930842805942519">"فوری ترتیبات میں چمکیلا پن دکھائیں"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"تیز ٹوگل فعال کریں"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"مجموعی جائزہ بٹن کے ذریعے پیجنگ فعال کریں"</string>
<string name="experimental" msgid="6198182315536726162">"تجرباتی"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"بلوٹوتھ آن کریں؟"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"اپنے کی بورڈ کو اپنے ٹیبلٹ کے ساتھ منسلک کرنے کیلئے پہلے آپ کو اپنا بلو ٹوتھ آن کرنا ہو گا۔"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 5ddd115..44bf45f 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Batareya quvvat olmayotgan vaqtda uning foizi holat qatorida ko‘rsatilsin"</string>
<string name="quick_settings" msgid="10042998191725428">"Tezkor sozlamalar"</string>
<string name="status_bar" msgid="4877645476959324760">"Holat qatori"</string>
+ <string name="overview" msgid="4018602013895926956">"Umumiy ma’lumot"</string>
<string name="demo_mode" msgid="2389163018533514619">"Demo rejim"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demo rejimni yoqish"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demo rejimni ko‘rsatish"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Holat panelida soat soniyalari ko‘rsatilsin. Bu batareya resursiga ta’sir qilishi mumkin."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Tezkor sozlamalarni qayta tartiblash"</string>
<string name="show_brightness" msgid="6613930842805942519">"Tezkor sozlamalarda yorqinlikni ko‘rsatish"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Tezkor almashtirishni yoqish"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Umumiy ma’lumot tugmasi orqali sahifalashni yoqish"</string>
<string name="experimental" msgid="6198182315536726162">"Tajribaviy"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth yoqilsinmi?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1a5cc8b..e825b11 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Hiển thị tỷ lệ phần trăm mức pin bên trong biểu tượng thanh trạng thái khi không sạc"</string>
<string name="quick_settings" msgid="10042998191725428">"Cài đặt nhanh"</string>
<string name="status_bar" msgid="4877645476959324760">"Thanh trạng thái"</string>
+ <string name="overview" msgid="4018602013895926956">"Tổng quan"</string>
<string name="demo_mode" msgid="2389163018533514619">"Chế độ trình diễn"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Bật chế độ trình diễn"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Hiển thị chế độ trình diễn"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Hiển thị giây đồng hồ trong thanh trạng thái. Có thể ảnh hưởng đến thời lượng pin."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Sắp xếp lại Cài đặt nhanh"</string>
<string name="show_brightness" msgid="6613930842805942519">"Hiển thị độ sáng trong Cài đặt nhanh"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Bật chuyển đổi nhanh"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Bật đánh số trang qua nút Tổng quan"</string>
<string name="experimental" msgid="6198182315536726162">"Thử nghiệm"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Bật Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Để kết nối bàn phím với máy tính bảng, trước tiên, bạn phải bật Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 0c6b2c9..ef7cd8b 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"未充电时在状态栏图标内显示电池电量百分比"</string>
<string name="quick_settings" msgid="10042998191725428">"快速设置"</string>
<string name="status_bar" msgid="4877645476959324760">"状态栏"</string>
+ <string name="overview" msgid="4018602013895926956">"概览"</string>
<string name="demo_mode" msgid="2389163018533514619">"演示模式"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"启用演示模式"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"显示演示模式"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在状态栏中显示时钟的秒数。这可能会影响电池的续航时间。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速设置"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速设置中显示亮度栏"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"启用快速切换"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"通过“概览”按钮启用分页功能"</string>
<string name="experimental" msgid="6198182315536726162">"实验性"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"要开启蓝牙吗?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"要将您的键盘连接到平板电脑,您必须先开启蓝牙。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ff97841..5d8b2d8 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"非充電時,在狀態列圖示顯示電量百分比"</string>
<string name="quick_settings" msgid="10042998191725428">"快速設定"</string>
<string name="status_bar" msgid="4877645476959324760">"狀態列"</string>
+ <string name="overview" msgid="4018602013895926956">"概覽"</string>
<string name="demo_mode" msgid="2389163018533514619">"示範模式"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"啟用示範模式"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"顯示示範模式"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數,但可能會影響電池壽命。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速設定顯示亮度"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"啟用快速切換"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"透過「概覽」按鈕啟用分頁"</string>
<string name="experimental" msgid="6198182315536726162">"實驗版"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"要開啟藍牙嗎?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"如要將鍵盤連接至平板電腦,請先開啟藍牙。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8d238d5..c90744c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"未充電時在狀態列圖示中顯示電量百分比"</string>
<string name="quick_settings" msgid="10042998191725428">"快速設定"</string>
<string name="status_bar" msgid="4877645476959324760">"狀態列"</string>
+ <string name="overview" msgid="4018602013895926956">"總覽"</string>
<string name="demo_mode" msgid="2389163018533514619">"示範模式"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"啟用示範模式"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"顯示示範模式"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"在狀態列中顯示時鐘秒數。這可能會影響電池續航力。"</string>
<string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
<string name="show_brightness" msgid="6613930842805942519">"在快速設定中顯示亮度"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"啟用快速切換"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"啟用透過 [總覽] 按鈕切換分頁的功能"</string>
<string name="experimental" msgid="6198182315536726162">"實驗性"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"要開啟藍牙功能嗎?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"如要將鍵盤連線到平板電腦,您必須先開啟藍牙。"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index f77e01a..9eff1e9 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -409,6 +409,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Bonisa amaphesenti eleveli yebhethri ngaphakathi kwesithonjana sebha yesimo uma kungashajwa"</string>
<string name="quick_settings" msgid="10042998191725428">"Izilungiselelo ezisheshayo"</string>
<string name="status_bar" msgid="4877645476959324760">"Ibha yesimo"</string>
+ <string name="overview" msgid="4018602013895926956">"Okufingqiwe"</string>
<string name="demo_mode" msgid="2389163018533514619">"Imodi yedemo"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Nika amandla imodi yedemo"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Bonisa imodi yedemo"</string>
@@ -437,6 +438,8 @@
<string name="clock_seconds_desc" msgid="6282693067130470675">"Bonisa amasekhondi wewashi kubha yesimo. Ingathinta impilo yebhethri."</string>
<string name="qs_rearrange" msgid="8060918697551068765">"Hlela kabusha izilungiselelo ezisheshayo"</string>
<string name="show_brightness" msgid="6613930842805942519">"Bonisa ukukhanya kuzilungiselelo ezisheshayo"</string>
+ <string name="overview_fast_toggle_via_button" msgid="8316769524084143500">"Nika amandla ukuguqula ngokushesha"</string>
+ <string name="overview_fast_toggle_via_button_desc" msgid="5477262463217629262">"Nika amandla wenkinobho yokubuka konke"</string>
<string name="experimental" msgid="6198182315536726162">"Okokulinga"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vula i-Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Ukuze uxhume ikhibhodi yakho nethebhulethi yakho, kufanele uqale ngokuvula i-Bluetooth."</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0c638a2..d8193ab 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -138,12 +138,6 @@
<!-- The duration in seconds to wait before the dismiss buttons are shown. -->
<integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
- <!-- The min animation duration for animating views that are currently visible. -->
- <integer name="recents_filter_animate_current_views_duration">250</integer>
-
- <!-- The min animation duration for animating views that are newly visible. -->
- <integer name="recents_filter_animate_new_views_duration">250</integer>
-
<!-- The duration of the window transition when coming to Recents from an app.
In order to defer the in-app animations until after the transition is complete,
we also need to use this value as the starting delay when animating the first
@@ -192,6 +186,16 @@
<!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
<integer name="recents_svelte_level">0</integer>
+ <!-- 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">-4</item>
+ <item name="recents_layout_focused_range_max" format="float" type="integer">3</item>
+
+ <!-- Recents: The relative range of visible tasks from the current scroll position
+ while the stack is not focused. -->
+ <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
+ <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
+
<!-- Whether to enable KeyguardService or not -->
<bool name="config_enableKeyguardService">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 73f63a9..c85ada8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -247,6 +247,12 @@
<!-- The amount to allow the stack to overscroll. -->
<dimen name="recents_stack_overscroll">24dp</dimen>
+ <!-- The size of the peek area at the top of the stack. -->
+ <dimen name="recents_layout_focused_peek_size">@dimen/recents_history_button_height</dimen>
+
+ <!-- The height of the history button. -->
+ <dimen name="recents_history_button_height">48dp</dimen>
+
<!-- Space reserved for the cards behind the top card in the top stack -->
<dimen name="top_stack_peek_amount">12dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d5f9557e..e0c0f6a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -698,8 +698,6 @@
<string name="recents_search_bar_label">search</string>
<!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
- <!-- Recents: Dismiss all button. [CHAR LIMIT=NONE] -->
- <string name="recents_dismiss_all_message">Dismiss all applications</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1067,6 +1065,9 @@
<!-- Name of status bar -->
<string name="status_bar">Status bar</string>
+ <!-- Name of overview -->
+ <string name="overview">Overview</string>
+
<!-- Name of demo mode (mode with preset icons for screenshots) -->
<string name="demo_mode">Demo mode</string>
@@ -1156,6 +1157,21 @@
<!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
<string name="qs_paging" translatable="false">Use the new Quick Settings</string>
+ <!-- Toggles paging recents via the recents button -->
+ <string name="overview_page_on_toggle">Enable paging</string>
+ <!-- Description for the toggle for fast-toggling recents via the recents button -->
+ <string name="overview_page_on_toggle_desc">Enable paging via the Overview button</string>
+
+ <!-- Toggles fast-toggling recents via the recents button -->
+ <string name="overview_fast_toggle_via_button">Enable fast toggle</string>
+ <!-- Description for the toggle for fast-toggling recents via the recents button -->
+ <string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
+
+ <!-- Toggles fullscreen screenshots -->
+ <string name="overview_fullscreen_thumbnails">Enable fullscreen screenshots</string>
+ <!-- Description for the toggle for fullscreen screenshots -->
+ <string name="overview_fullscreen_thumbnails_desc">Enable fullscreen screenshots in Overview</string>
+
<!-- Category in the System UI Tuner settings, where new/experimental
settings are -->
<string name="experimental">Experimental</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4462a03..2fd0fe5 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -220,9 +220,8 @@
<item name="android:colorControlActivated">@color/system_accent_color</item>
</style>
- <style name="systemui_theme_light" parent="@android:style/Theme.DeviceDefault.Light">
- <item name="android:colorPrimary">@color/system_primary_color</item>
- <item name="android:colorControlActivated">@color/system_accent_color</item>
+ <style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:colorControlActivated">@android:color/white</item>
</style>
<style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 5980108..4d07d5f 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -21,10 +21,6 @@
<PreferenceScreen
android:title="@string/quick_settings">
- <Preference
- android:key="qs_tuner"
- android:title="@string/qs_rearrange" />
-
<PreferenceCategory
android:title="@string/experimental">
@@ -87,6 +83,27 @@
</PreferenceScreen>
+
+ <PreferenceScreen
+ android:title="@string/overview" >
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="overview_page_on_toggle"
+ android:title="@string/overview_page_on_toggle"
+ android:summary="@string/overview_page_on_toggle_desc" />
+
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="overview_fast_toggle"
+ 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_fullscreen_thumbnails"
+ android:title="@string/overview_fullscreen_thumbnails"
+ android:summary="@string/overview_fullscreen_thumbnails_desc" />
+
+ </PreferenceScreen>
+
<SwitchPreference
android:key="battery_pct"
android:title="@string/show_battery_percentage"
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 3657cf2..9d98772 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -30,7 +30,8 @@
@Retention(RetentionPolicy.SOURCE)
@StringDef({
- Key.SEARCH_APP_WIDGET_ID,
+ Key.OVERVIEW_SEARCH_APP_WIDGET_ID,
+ Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
Key.DEBUG_MODE_ENABLED,
Key.HOTSPOT_TILE_LAST_USED,
Key.COLOR_INVERSION_TILE_LAST_USED,
@@ -43,8 +44,8 @@
Key.DND_FAVORITE_ZEN,
})
public @interface Key {
- String SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
- String SEARCH_APP_WIDGET_PACKAGE = "searchAppWidgetPackage";
+ String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
+ String OVERVIEW_SEARCH_APP_WIDGET_PACKAGE = "searchAppWidgetPackage";
String DEBUG_MODE_ENABLED = "debugModeEnabled";
String HOTSPOT_TILE_LAST_USED = "HotspotTileLastUsed";
String COLOR_INVERSION_TILE_LAST_USED = "ColorInversionTileLastUsed";
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 9a4cd93..4cb8a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.graphics.Rect;
import android.view.Display;
import android.view.View;
@@ -31,5 +32,20 @@
/**
* Docks the top-most task and opens recents.
*/
- void dockTopTask();
+ void dockTopTask(boolean draggingInRecents, Rect initialBounds);
+
+ /**
+ * Called during a drag-from-navbar-in gesture.
+ *
+ * @param distanceFromTop the distance of the current drag in gesture from the top of the
+ * screen
+ */
+ void onDraggingInRecents(float distanceFromTop);
+
+ /**
+ * Called when the gesture to drag in recents ended.
+ *
+ * @param velocity the velocity of the finger when releasing it in pixels per second
+ */
+ void onDraggingInRecentsEnded(float velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 949efc5..19e299254 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -48,12 +48,12 @@
com.android.systemui.keyguard.KeyguardViewMediator.class,
com.android.systemui.recents.Recents.class,
com.android.systemui.volume.VolumeUI.class,
+ Divider.class,
com.android.systemui.statusbar.SystemBars.class,
com.android.systemui.usb.StorageNotification.class,
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.keyboard.KeyboardUI.class,
- Divider.class
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7ef5187..eee685f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -379,6 +379,13 @@
@Override
public void onDeviceProvisioned() {
sendUserPresentBroadcast();
+ synchronized (KeyguardViewMediator.this) {
+ // If system user is provisioned, we might want to lock now to avoid showing launcher
+ if (UserManager.isSplitSystemUser()
+ && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+ doKeyguardLocked(null);
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 47189b0..6d8b476 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -111,7 +111,9 @@
private void handleRefreshState() {
mIsIconVisible = mSecurityController.isVpnEnabled();
- if (mSecurityController.hasDeviceOwner()) {
+ // If the device has device owner, show "Device may be monitored", but --
+ // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
+ if (mSecurityController.isDeviceManaged()) {
mFooterTextId = R.string.device_owned_footer;
mIsVisible = true;
} else {
@@ -156,6 +158,8 @@
private String getMessage(String deviceOwner, String profileOwner, String primaryVpn,
String profileVpn, boolean primaryUserIsManaged) {
+ // Show a special warning when the device has device owner, but --
+ // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
if (deviceOwner != null) {
if (primaryVpn != null) {
return mContext.getString(R.string.monitoring_description_vpn_app_device_owned,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 049754e..bb2b8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -77,9 +77,9 @@
private Record mDetailRecord;
private Callback mCallback;
private BrightnessController mBrightnessController;
- private QSTileHost mHost;
+ protected QSTileHost mHost;
- private QSFooter mFooter;
+ protected QSFooter mFooter;
private boolean mGridContentVisible = true;
protected LinearLayout mQsContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 5d928d6..7f45545 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -44,8 +44,8 @@
* state update pass on tile looper.
*/
public abstract class QSTile<TState extends State> implements Listenable {
- protected final String TAG = "QSTile." + getClass().getSimpleName();
- protected static final boolean DEBUG = Log.isLoggable("QSTile", Log.DEBUG);
+ protected final String TAG = "Tile." + getClass().getSimpleName();
+ protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
protected final Host mHost;
protected final Context mContext;
@@ -332,7 +332,7 @@
Looper getLooper();
Context getContext();
Collection<QSTile<?>> getTiles();
- void setCallback(Callback callback);
+ void addCallback(Callback callback);
BluetoothController getBluetoothController();
LocationController getLocationController();
RotationLockController getRotationLockController();
@@ -453,9 +453,9 @@
public static class State {
public boolean visible;
public Icon icon;
- public String label;
- public String contentDescription;
- public String dualLabelContentDescription;
+ public CharSequence label;
+ public CharSequence contentDescription;
+ public CharSequence dualLabelContentDescription;
public boolean autoMirrorDrawable = true;
public boolean copyTo(State other) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
new file mode 100644
index 0000000..55f4736
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
@@ -0,0 +1,76 @@
+package com.android.systemui.qs;
+
+import android.os.IBinder;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.util.Log;
+
+
+public class QSTileServiceWrapper implements IQSTileService {
+ private static final String TAG = "IQSTileServiceWrapper";
+
+ private final IQSTileService mService;
+
+ public QSTileServiceWrapper(IQSTileService service) {
+ mService = service;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mService.asBinder();
+ }
+
+ @Override
+ public void setQSTile(Tile tile) {
+ try {
+ mService.setQSTile(tile);
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+
+ @Override
+ public void onTileAdded() {
+ try {
+ mService.onTileAdded();
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+
+ @Override
+ public void onTileRemoved() {
+ try {
+ mService.onTileRemoved();
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+
+ @Override
+ public void onStartListening() {
+ try {
+ mService.onStartListening();
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+
+ @Override
+ public void onStopListening() {
+ try {
+ mService.onStopListening();
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+
+ @Override
+ public void onClick() {
+ try {
+ mService.onClick();
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from QSTileService", e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index cc264a0..f32cfdc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -24,22 +25,16 @@
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
-
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.AnimationIcon;
-import com.android.systemui.qs.QSTile.State;
import java.util.Objects;
@@ -227,6 +222,7 @@
final ImageView icon = new ImageView(mContext);
icon.setId(android.R.id.icon);
icon.setScaleType(ScaleType.CENTER_INSIDE);
+ icon.setImageTintList(ColorStateList.valueOf(getContext().getColor(android.R.color.white)));
return icon;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
new file mode 100644
index 0000000..a4ff685
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
@@ -0,0 +1,88 @@
+/*
+ * 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.qs.customize;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+
+public class BlankCustomTile extends QSTile<QSTile.State> {
+ public static final String PREFIX = "custom(";
+
+ private final ComponentName mComponent;
+
+ private BlankCustomTile(Host host, String action) {
+ super(host);
+ mComponent = ComponentName.unflattenFromString(action);
+ }
+
+ public static QSTile<?> create(Host host, String spec) {
+ if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
+ throw new IllegalArgumentException("Bad custom tile spec: " + spec);
+ }
+ final String action = spec.substring(PREFIX.length(), spec.length() - 1);
+ if (action.isEmpty()) {
+ throw new IllegalArgumentException("Empty custom tile spec action");
+ }
+ return new BlankCustomTile(host, action);
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ }
+
+ @Override
+ protected State newTileState() {
+ return new State();
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ super.handleUserSwitch(newUserId);
+ }
+
+ @Override
+ protected void handleClick() {
+ MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
+ }
+
+ @Override
+ protected void handleLongClick() {
+ }
+
+ @Override
+ protected void handleUpdateState(State state, Object arg) {
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ServiceInfo info = pm.getServiceInfo(mComponent, 0);
+ state.visible = true;
+ state.icon = new DrawableIcon(info.loadIcon(pm));
+ state.label = info.loadLabel(pm).toString();
+ state.contentDescription = state.label;
+ } catch (Exception e) {
+ state.visible = false;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_INTENT;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 8866e55..422ae4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -15,16 +15,34 @@
*/
package com.android.systemui.qs.customize;
+import android.app.ActivityManager;
+import android.app.Service;
import android.content.ClipData;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.service.quicksettings.IQSTileService;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileServiceWrapper;
+import com.android.systemui.qs.tiles.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.TunerService;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
/**
* A version of QSPanel that allows tiles to be dragged around rather than
@@ -32,8 +50,14 @@
* and the saving/ordering is handled by the CustomQSTileHost.
*/
public class CustomQSPanel extends QSPanel {
+
+ private static final String TAG = "CustomQSPanel";
- private CustomQSTileHost mCustomHost;
+ private List<String> mSavedTiles;
+ private ArrayList<String> mStash;
+ private List<String> mTiles = new ArrayList<>();
+
+ private ArrayList<QSTile<?>> mCurrentTiles = new ArrayList<>();
public CustomQSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -41,12 +65,9 @@
.inflate(R.layout.qs_customize_layout, mQsContainer, false);
mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
- }
+ removeView(mFooter.getView());
- @Override
- public void setHost(QSTileHost host) {
- super.setHost(host);
- mCustomHost = (CustomQSTileHost) host;
+ TunerService.get(mContext).addTunable(this, QSTileHost.TILES_SETTING);
}
@Override
@@ -55,17 +76,16 @@
// No Brightness for you.
super.onTuningChanged(key, "0");
}
- }
-
- public CustomQSTileHost getCustomHost() {
- return mCustomHost;
+ if (QSTileHost.TILES_SETTING.equals(key)) {
+ mSavedTiles = QSTileHost.loadTileSpecs(mContext, newValue);
+ }
}
public void tileSelected(QSTile<?> tile, ClipData currentClip) {
String sourceSpec = getSpec(currentClip);
String destSpec = tile.getTileSpec();
if (!sourceSpec.equals(destSpec)) {
- mCustomHost.moveTo(sourceSpec, destSpec);
+ moveTo(sourceSpec, destSpec);
}
}
@@ -79,4 +99,124 @@
public String getSpec(ClipData data) {
return data.getItemAt(0).getText().toString();
}
+
+ public void setSavedTiles() {
+ setTiles(mSavedTiles);
+ }
+
+ public void saveCurrentTiles() {
+ for (int i = 0; i < mSavedTiles.size(); i++) {
+ String tileSpec = mSavedTiles.get(i);
+ if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
+ if (!mTiles.contains(tileSpec)) {
+ mContext.bindServiceAsUser(
+ new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
+ new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
+ IQSTileService.Stub.asInterface(service));
+ wrapper.onStopListening();
+ wrapper.onTileRemoved();
+ mContext.unbindService(this);
+ }
+ }, Service.BIND_AUTO_CREATE,
+ new UserHandle(ActivityManager.getCurrentUser()));
+ }
+ }
+ for (int i = 0; i < mTiles.size(); i++) {
+ String tileSpec = mTiles.get(i);
+ if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
+ if (!mSavedTiles.contains(tileSpec)) {
+ mContext.bindServiceAsUser(
+ new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
+ new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
+ IQSTileService.Stub.asInterface(service));
+ wrapper.onTileAdded();
+ mContext.unbindService(this);
+ }
+ }, Service.BIND_AUTO_CREATE,
+ new UserHandle(ActivityManager.getCurrentUser()));
+ }
+ }
+ Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
+ TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
+ }
+
+ public void stashCurrentTiles() {
+ mStash = new ArrayList<>(mTiles);
+ }
+
+ public void unstashTiles() {
+ setTiles(mStash);
+ }
+
+ @Override
+ public void setTiles(Collection<QSTile<?>> tiles) {
+ setTilesInternal();
+ }
+
+ private void setTilesInternal() {
+ for (int i = 0; i < mCurrentTiles.size(); i++) {
+ mCurrentTiles.get(i).destroy();
+ }
+ mCurrentTiles.clear();
+ for (int i = 0; i < mTiles.size(); i++) {
+ if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
+ mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i)));
+ } else {
+ mCurrentTiles.add(mHost.createTile(mTiles.get(i)));
+ }
+ mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i));
+ }
+ super.setTiles(mCurrentTiles);
+ }
+
+ public void addTile(String spec) {
+ mTiles.add(spec);
+ setTilesInternal();
+ }
+
+ public void moveTo(String from, String to) {
+ int fromIndex = mTiles.indexOf(from);
+ if (fromIndex < 0) {
+ Log.e(TAG, "Unknown from tile " + from);
+ return;
+ }
+ int index = mTiles.indexOf(to);
+ if (index < 0) {
+ Log.e(TAG, "Unknown to tile " + to);
+ return;
+ }
+ mTiles.remove(fromIndex);
+ mTiles.add(index, from);
+ setTilesInternal();
+ }
+
+ public void remove(String spec) {
+ if (!mTiles.remove(spec)) {
+ Log.e(TAG, "Unknown remove spec " + spec);
+ }
+ setTilesInternal();
+ }
+
+ public void setTiles(List<String> tiles) {
+ mTiles = new ArrayList<>(tiles);
+ setTilesInternal();
+ }
+
+ public Collection<QSTile<?>> getTiles() {
+ return mCurrentTiles;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
deleted file mode 100644
index 3f85982..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ /dev/null
@@ -1,194 +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.qs.customize;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.statusbar.policy.SecurityController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @see CustomQSPanel
- */
-public class CustomQSTileHost extends QSTileHost {
-
- private static final String TAG = "CustomHost";
- private List<String> mTiles;
- private List<String> mSavedTiles;
- private ArrayList<String> mStash;
-
- public CustomQSTileHost(Context context, QSTileHost host) {
- super(context, null, host.getBluetoothController(), host.getLocationController(),
- host.getRotationLockController(), host.getNetworkController(),
- host.getZenModeController(), host.getHotspotController(), host.getCastController(),
- host.getFlashlightController(), host.getUserSwitcherController(),
- host.getUserInfoController(), host.getKeyguardMonitor(),
- new BlankSecurityController(), host.getBatteryController());
- }
-
- @Override
- public QSTile<?> createTile(String tileSpec) {
- QSTile<?> tile = super.createTile(tileSpec);
- tile.setTileSpec(tileSpec);
- return tile;
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- // No Tunings For You.
- if (TILES_SETTING.equals(key)) {
- mSavedTiles = super.loadTileSpecs(newValue);
- }
- }
-
- public void setSavedTiles() {
- setTiles(mSavedTiles);
- }
-
- public void saveCurrentTiles() {
- Secure.putStringForUser(getContext().getContentResolver(), TILES_SETTING,
- TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
- }
-
- public void stashCurrentTiles() {
- mStash = new ArrayList<>(mTiles);
- }
-
- public void unstashTiles() {
- setTiles(mStash);
- }
-
- public void moveTo(String from, String to) {
- int fromIndex = mTiles.indexOf(from);
- if (fromIndex < 0) {
- Log.e(TAG, "Unknown from tile " + from);
- return;
- }
- int index = mTiles.indexOf(to);
- if (index < 0) {
- Log.e(TAG, "Unknown to tile " + to);
- return;
- }
- mTiles.remove(fromIndex);
- mTiles.add(index, from);
- super.onTuningChanged(TILES_SETTING, null);
- }
-
- public void remove(String spec) {
- if (!mTiles.remove(spec)) {
- Log.e(TAG, "Unknown remove spec " + spec);
- }
- super.onTuningChanged(TILES_SETTING, null);
- }
-
- public void setTiles(List<String> tiles) {
- mTiles = new ArrayList<>(tiles);
- super.onTuningChanged(TILES_SETTING, null);
- }
-
- @Override
- protected List<String> loadTileSpecs(String tileList) {
- return mTiles;
- }
-
- public void addTile(String spec) {
- mTiles.add(spec);
- super.onTuningChanged(TILES_SETTING, null);
- }
-
- public void replace(String oldTile, String newTile) {
- if (oldTile.equals(newTile)) {
- return;
- }
- MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REORDER, oldTile + ","
- + newTile);
- List<String> order = new ArrayList<>(mTileSpecs);
- int index = order.indexOf(oldTile);
- if (index < 0) {
- Log.e(TAG, "Can't find " + oldTile);
- return;
- }
- order.remove(newTile);
- order.add(index, newTile);
- setTiles(order);
- }
-
- /**
- * Blank so that the customizing QS view doesn't show any security messages in the footer.
- */
- private static class BlankSecurityController implements SecurityController {
- @Override
- public boolean hasDeviceOwner() {
- return false;
- }
-
- @Override
- public boolean hasProfileOwner() {
- return false;
- }
-
- @Override
- public String getDeviceOwnerName() {
- return null;
- }
-
- @Override
- public String getProfileOwnerName() {
- return null;
- }
-
- @Override
- public boolean isVpnEnabled() {
- return false;
- }
-
- @Override
- public boolean isVpnRestricted() {
- return false;
- }
-
- @Override
- public String getPrimaryVpnName() {
- return null;
- }
-
- @Override
- public String getProfileVpnName() {
- return null;
- }
-
- @Override
- public void onUserSwitched(int newUserId) {
- }
-
- @Override
- public void addCallback(SecurityControllerCallback callback) {
- }
-
- @Override
- public void removeCallback(SecurityControllerCallback callback) {
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index 1669278..d0d5b54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -83,8 +83,8 @@
record.tileView.setVisibility(View.VISIBLE);
record.tileView.init(null, null, null);
record.tileView.setOnTouchListener(this);
- if (mCurrentClip != null
- && mCurrentClip.getItemAt(0).getText().toString().equals(record.tile.getTileSpec())) {
+ if (mCurrentClip != null && mCurrentClip.getItemAt(0)
+ .getText().toString().equals(record.tile.getTileSpec())) {
record.tileView.setAlpha(.3f);
mCurrentView = record.tileView;
}
@@ -180,7 +180,7 @@
case MotionEvent.ACTION_DOWN:
// Stash the current tiles, in case the drop is on info, that we can restore
// the previous state.
- mPanel.getCustomHost().stashCurrentTiles();
+ mPanel.stashCurrentTiles();
mCurrentView = v;
mCurrentClip = mPanel.getClip((QSTile<?>) v.getTag());
View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index b5a885c..baad370 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -22,6 +22,7 @@
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnDismissListener;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.DragEvent;
@@ -71,11 +72,11 @@
private CustomQSPanel mQsPanel;
private boolean isShown;
- private CustomQSTileHost mHost;
private DropButton mInfoButton;
private DropButton mRemoveButton;
private FloatingActionButton mFab;
private SystemUIDialog mDialog;
+ private QSTileHost mHost;
public QSCustomizer(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
@@ -85,11 +86,11 @@
}
public void setHost(QSTileHost host) {
- mHost = new CustomQSTileHost(mContext, host);
- mHost.setCallback(this);
+ mHost = host;
+ mHost.addCallback(this);
mQsPanel.setTiles(mHost.getTiles());
mQsPanel.setHost(mHost);
- mHost.setSavedTiles();
+ mQsPanel.setSavedTiles();
}
@Override
@@ -129,7 +130,7 @@
public void show(int x, int y) {
isShown = true;
- mHost.setSavedTiles();
+ mQsPanel.setSavedTiles();
mPhoneStatusBar.getStatusBarWindow().addView(this);
mQsPanel.setListening(true);
mClipper.animateCircularClip(x, y, true, this);
@@ -150,7 +151,7 @@
for (String tile : QSPagingSwitch.QS_PAGE_TILES.split(",")) {
tiles.add(tile);
}
- mHost.setTiles(tiles);
+ mQsPanel.setTiles(tiles);
}
private void setDragging(boolean dragging) {
@@ -158,7 +159,8 @@
}
private void save() {
- mHost.saveCurrentTiles();
+ Log.d("CustomQSPanel", "Save!");
+ mQsPanel.saveCurrentTiles();
// TODO: At save button.
hide(0, 0);
}
@@ -167,6 +169,7 @@
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_SAVE:
+ Log.d("CustomQSPanel", "Save...");
save();
break;
case MENU_RESET:
@@ -179,7 +182,7 @@
@Override
public void onTileSelected(String spec) {
if (mDialog != null) {
- mHost.addTile(spec);
+ mQsPanel.addTile(spec);
mDialog.dismiss();
}
}
@@ -203,9 +206,9 @@
public void onDrop(View v, ClipData data) {
if (v == mRemoveButton) {
- mHost.remove(mQsPanel.getSpec(data));
+ mQsPanel.remove(mQsPanel.getSpec(data));
} else if (v == mInfoButton) {
- mHost.unstashTiles();
+ mQsPanel.unstashTiles();
SystemUIDialog dialog = new SystemUIDialog(mContext);
dialog.setTitle(mQsPanel.getSpec(data));
dialog.setPositiveButton(R.string.ok, null);
@@ -220,7 +223,7 @@
android.R.style.Theme_Material_Dialog);
View view = LayoutInflater.from(mContext).inflate(R.layout.qs_add_tiles_list, null);
ListView listView = (ListView) view.findViewById(android.R.id.list);
- TileAdapter adapter = new TileAdapter(mContext, mHost.getTiles(), mHost);
+ TileAdapter adapter = new TileAdapter(mContext, mQsPanel.getTiles(), mHost);
adapter.setListener(this);
listView.setDivider(null);
listView.setDividerHeight(0);
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 579f58d..144b202 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -27,6 +27,7 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
+import android.service.quicksettings.TileService;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -82,8 +83,10 @@
continue;
}
if (tileSpecs.contains(spec)) {
+ Log.d(TAG, "Skipping " + spec);
continue;
}
+ Log.d(TAG, "Trying " + spec);
final QSTile<?> tile = host.createTile(spec);
// Bad, bad, very bad.
tile.setListening(true);
@@ -156,7 +159,7 @@
Log.d(TAG, "Added " + mLabel);
}
- private void addTile(String spec, Drawable icon, String label) {
+ private void addTile(String spec, Drawable icon, CharSequence label) {
TileInfo info = new TileInfo();
info.label = label;
info.drawable = icon;
@@ -164,7 +167,7 @@
mTiles.add(info);
}
- private void addTile(String spec, Icon icon, String label, Context context) {
+ private void addTile(String spec, Icon icon, CharSequence label, Context context) {
addTile(spec, icon.getDrawable(context), label);
}
@@ -208,19 +211,17 @@
private static class TileInfo {
private String spec;
private Drawable drawable;
- private String label;
+ private CharSequence label;
}
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
- // TODO: Become non-prototype and an API.
- private static final String TILE_ACTION = "android.intent.action.QS_TILE";
-
@Override
protected Collection<TileGroup> doInBackground(Void... params) {
HashMap<String, TileGroup> pkgMap = new HashMap<>();
PackageManager pm = mContext.getPackageManager();
// TODO: Handle userness.
- List<ResolveInfo> services = pm.queryIntentServices(new Intent(TILE_ACTION), 0);
+ List<ResolveInfo> services = pm.queryIntentServices(
+ new Intent(TileService.ACTION_QS_TILE), 0);
for (ResolveInfo info : services) {
String packageName = info.serviceInfo.packageName;
ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index fd70d02..7f07ddc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -133,7 +133,7 @@
R.string.accessibility_quick_settings_bluetooth_off);
}
- String bluetoothName = state.label;
+ CharSequence bluetoothName = state.label;
if (connected) {
bluetoothName = state.dualLabelContentDescription = mContext.getString(
R.string.accessibility_bluetooth_name, state.label);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e2d2ffb..a0bbbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -23,16 +23,14 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.net.MobileDataController;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTileBaseView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataController;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataController.DataUsageInfo;
import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
/** Quick settings tile: Cellular **/
@@ -247,7 +245,7 @@
final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
? convertView
: LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
- final DataUsageInfo info = mDataController.getDataUsageInfo();
+ final MobileDataController.DataUsageInfo info = mDataController.getDataUsageInfo();
if (info == null) return v;
v.bind(info);
return v;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
index cf76ed4..b0d885a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -16,36 +16,93 @@
package com.android.systemui.qs.tiles;
+import android.app.ActivityManager;
+import android.app.Service;
import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.util.Log;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileServiceWrapper;
+import com.android.systemui.statusbar.phone.QSTileHost;
public class CustomTile extends QSTile<QSTile.State> {
public static final String PREFIX = "custom(";
- private final ComponentName mComponent;
+ // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot.
+ // So instead we have a period of waiting.
+ private static final long UNBIND_DELAY = 30000;
- private CustomTile(Host host, String action) {
+ private final ComponentName mComponent;
+ private final Tile mTile;
+
+ private QSTileServiceWrapper mService;
+ private boolean mListening;
+ private boolean mBound;
+
+ private CustomTile(QSTileHost host, String action) {
super(host);
mComponent = ComponentName.unflattenFromString(action);
+ mTile = new Tile(mComponent, host);
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ServiceInfo info = pm.getServiceInfo(mComponent, 0);
+ mTile.setIcon(android.graphics.drawable.Icon
+ .createWithResource(mComponent.getPackageName(), info.icon));
+ mTile.setLabel(info.loadLabel(pm));
+ } catch (Exception e) {
+ }
}
- public static QSTile<?> create(Host host, String spec) {
- if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
- throw new IllegalArgumentException("Bad intent tile spec: " + spec);
- }
- final String action = spec.substring(PREFIX.length(), spec.length() - 1);
- if (action.isEmpty()) {
- throw new IllegalArgumentException("Empty intent tile spec action");
- }
- return new CustomTile(host, action);
+ public ComponentName getComponent() {
+ return mComponent;
+ }
+
+ public Tile getQsTile() {
+ return mTile;
+ }
+
+ public void updateState(Tile tile) {
+ Log.d("TileService", "Setting state " + tile.getLabel());
+ mTile.setIcon(tile.getIcon());
+ mTile.setLabel(tile.getLabel());
+ mTile.setContentDescription(tile.getContentDescription());
}
@Override
public void setListening(boolean listening) {
+ if (mListening == listening) return;
+ mListening = listening;
+ if (listening) {
+ mHandler.removeCallbacks(mUnbind);
+ if (!mBound) {
+ // TODO: Guarantee re-bind on user-switch.
+ mContext.bindServiceAsUser(new Intent().setComponent(mComponent),
+ mServiceConnection, Service.BIND_AUTO_CREATE,
+ new UserHandle(ActivityManager.getCurrentUser()));
+ mBound = true;
+ }
+ } else {
+ if (mService!= null) {
+ mService.onStopListening();
+ }
+ mHandler.postDelayed(mUnbind, UNBIND_DELAY);
+ }
+ }
+
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ mHandler.removeCallbacks(mUnbind);
+ mUnbind.run();
}
@Override
@@ -60,6 +117,11 @@
@Override
protected void handleClick() {
+ if (mService != null) {
+ mService.onClick();
+ } else {
+ Log.e(TAG, "Click with no service " + getTileSpec());
+ }
MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
}
@@ -69,16 +131,13 @@
@Override
protected void handleUpdateState(State state, Object arg) {
- // TODO: Actual things.
- try {
- PackageManager pm = mContext.getPackageManager();
- ServiceInfo info = pm.getServiceInfo(mComponent, 0);
- state.visible = true;
- state.icon = new DrawableIcon(info.loadIcon(pm));
- state.label = info.loadLabel(pm).toString();
+ state.visible = true;
+ state.icon = new DrawableIcon(mTile.getIcon().loadDrawable(mContext));
+ state.label = mTile.getLabel();
+ if (mTile.getContentDescription() != null) {
+ state.contentDescription = mTile.getContentDescription();
+ } else {
state.contentDescription = state.label;
- } catch (Exception e) {
- state.visible = false;
}
}
@@ -86,4 +145,48 @@
public int getMetricsCategory() {
return MetricsLogger.QS_INTENT;
}
+
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service));
+ if (mListening) {
+ mService.setQSTile(mTile);
+ mService.onStartListening();
+ } else {
+ mService.onStopListening();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+
+ private final Runnable mUnbind = new Runnable() {
+ @Override
+ public void run() {
+ mContext.unbindService(mServiceConnection);
+ mBound = false;
+ }
+ };
+
+ public static ComponentName getComponentFromSpec(String spec) {
+ final String action = spec.substring(PREFIX.length(), spec.length() - 1);
+ if (action.isEmpty()) {
+ throw new IllegalArgumentException("Empty custom tile spec action");
+ }
+ return ComponentName.unflattenFromString(action);
+ }
+
+ public static QSTile<?> create(QSTileHost host, String spec) {
+ if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
+ throw new IllegalArgumentException("Bad custom tile spec: " + spec);
+ }
+ final String action = spec.substring(PREFIX.length(), spec.length() - 1);
+ if (action.isEmpty()) {
+ throw new IllegalArgumentException("Empty custom tile spec action");
+ }
+ return new CustomTile(host, action);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
index d0ae383..d814b1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
@@ -23,11 +23,10 @@
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
-
+import com.android.settingslib.net.MobileDataController;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.DataUsageGraph;
-import com.android.systemui.statusbar.policy.NetworkController;
import java.text.DecimalFormat;
@@ -60,7 +59,7 @@
R.dimen.qs_data_usage_text_size);
}
- public void bind(NetworkController.MobileDataController.DataUsageInfo info) {
+ public void bind(MobileDataController.DataUsageInfo info) {
final Resources res = mContext.getResources();
final int titleId;
final long bytes;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 3763618..7f4442a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -166,7 +166,7 @@
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_wifi,
signalContentDescription);
- String wifiName = state.label;
+ CharSequence wifiName = state.label;
if (state.connected) {
wifiName = r.getString(R.string.accessibility_wifi_name, state.label);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index a429447..c08fb05 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -29,34 +29,4 @@
public static final int DismissSourceHeaderButton = 2;
}
- // TODO: Move into RecentsDebugFlags
- public static class DebugFlags {
-
- public static class App {
- // Enables debug drawing for the transition thumbnail
- public static final boolean EnableTransitionThumbnailDebugMode = false;
- // Enables the filtering of tasks according to their grouping
- public static final boolean EnableTaskFiltering = false;
- // Enables dismiss-all
- public static final boolean EnableDismissAll = false;
- // Enables fast-toggling by just tapping on the recents button
- public static final boolean EnableFastToggleRecents = false;
- // Enables the thumbnail alpha on the front-most task
- public static final boolean EnableThumbnailAlphaOnFrontmost = false;
- // This disables the search bar integration
- public static final boolean DisableSearchBar = true;
- // This disables the bitmap and icon caches
- public static final boolean DisableBackgroundCache = false;
- // Enables the simulated task affiliations
- public static final boolean EnableSimulatedTaskGroups = false;
- // Defines the number of mock task affiliations per group
- public static final int TaskAffiliationsGroupCount = 12;
- // Enables us to create mock recents tasks
- public static final boolean EnableSystemServicesProxy = false;
- // Defines the number of mock recents packages to create
- public static final int SystemServicesProxyMockPackageCount = 3;
- // Defines the number of mock recents tasks to create
- public static final int SystemServicesProxyMockTaskCount = 100;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java
index f187178..8ae8c53 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.recents;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * Event sent when the exit animation is started.
+ *
+ * This is sent so parts of UI can synchronize on this event and adjust their appearance. An example
+ * of that is hiding the tasks when the launched application window becomes visible.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+public class ExitRecentsWindowFirstAnimationFrameEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index 79eca30d..b36b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -24,8 +24,11 @@
oneway interface IRecentsNonSystemUserCallbacks {
void preloadRecents();
void cancelPreloadingRecents();
- void showRecents(boolean triggeredFromAltTab);
+ void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
+ boolean reloadTasks);
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecents();
void onConfigurationChanged();
+ void onDraggingInRecents(float distanceFromTop);
+ void onDraggingInRecentsEnded(float velocity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a58bc58..c98ecb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,12 +21,14 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.Display;
import android.view.View;
@@ -61,6 +63,7 @@
private final static String ACTION_TOGGLE_RECENTS = "com.android.systemui.recents.ACTION_TOGGLE";
private static SystemServicesProxy sSystemServicesProxy;
+ private static RecentsDebugFlags sDebugFlags;
private static RecentsTaskLoader sTaskLoader;
private static RecentsConfiguration sConfiguration;
@@ -71,6 +74,7 @@
private Handler mHandler;
private RecentsImpl mImpl;
+ private int mDraggingInRecentsCurrentUser;
// Only For system user, this is the callbacks instance we return to each secondary user
private RecentsSystemUser mSystemUserCallbacks;
@@ -147,8 +151,13 @@
return sConfiguration;
}
+ public static RecentsDebugFlags getDebugFlags() {
+ return sDebugFlags;
+ }
+
@Override
public void start() {
+ sDebugFlags = new RecentsDebugFlags(mContext);
sSystemServicesProxy = new SystemServicesProxy(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
@@ -165,6 +174,7 @@
// Register with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+ EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
// Due to the fact that RecentsActivity is per-user, we need to establish and interface for
@@ -193,20 +203,28 @@
*/
@Override
public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (proxyToOverridePackage(ACTION_SHOW_RECENTS)) {
return;
}
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.showRecents(triggeredFromAltTab);
+ mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
+ true /* animate */, false /* reloadTasks */);
} else {
if (mSystemUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
if (callbacks != null) {
try {
- callbacks.showRecents(triggeredFromAltTab);
+ callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
+ true /* animate */, false /* reloadTasks */);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
@@ -222,6 +240,12 @@
*/
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (proxyToOverridePackage(ACTION_HIDE_RECENTS)) {
return;
}
@@ -251,6 +275,12 @@
*/
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (proxyToOverridePackage(ACTION_TOGGLE_RECENTS)) {
return;
}
@@ -280,6 +310,12 @@
*/
@Override
public void preloadRecents() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.preloadRecents();
@@ -302,6 +338,12 @@
@Override
public void cancelPreloadingRecents() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.cancelPreloadingRecents();
@@ -323,17 +365,78 @@
}
@Override
- public void dockTopTask() {
- mImpl.dockTopTask();
+ public void dockTopTask(boolean draggingInRecents, Rect initialBounds) {
+ mImpl.dockTopTask(draggingInRecents, initialBounds);
+ if (draggingInRecents) {
+ mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
+ }
+ }
+
+ @Override
+ public void onDraggingInRecents(float distanceFromTop) {
+ if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
+ mImpl.onDraggingInRecents(distanceFromTop);
+ } else {
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(
+ mDraggingInRecentsCurrentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.onDraggingInRecents(distanceFromTop);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: "
+ + mDraggingInRecentsCurrentUser);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDraggingInRecentsEnded(float velocity) {
+ if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) {
+ mImpl.onDraggingInRecentsEnded(velocity);
+ } else {
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(
+ mDraggingInRecentsCurrentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.onDraggingInRecentsEnded(velocity);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: "
+ + mDraggingInRecentsCurrentUser);
+ }
+ }
+ }
}
@Override
public void showNextAffiliatedTask() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
mImpl.showNextAffiliatedTask();
}
@Override
public void showPrevAffiliatedTask() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
mImpl.showPrevAffiliatedTask();
}
@@ -386,8 +489,8 @@
* Handle screen pinning request.
*/
public final void onBusEvent(final ScreenPinningRequestEvent event) {
- int processUser = event.systemServicesProxy.getProcessUser();
- if (event.systemServicesProxy.isSystemUser(processUser)) {
+ int processUser = sSystemServicesProxy.getProcessUser();
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
mImpl.onStartScreenPinning(event.applicationContext);
} else {
postToSystemUser(new Runnable() {
@@ -456,6 +559,14 @@
}
/**
+ * @return whether this device is provisioned.
+ */
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ }
+
+ /**
* Attempts to proxy the following action to the override recents package.
* @return whether the proxying was successful
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 6874247..16b1592 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -37,21 +37,28 @@
import android.view.View;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
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.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -75,67 +82,71 @@
/**
* The main Recents activity that is started from AlternateRecentsComponent.
*/
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- ViewTreeObserver.OnPreDrawListener {
+public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener {
private final static String TAG = "RecentsActivity";
private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- RecentsPackageMonitor mPackageMonitor;
- long mLastTabKeyEventTime;
- boolean mFinishedOnStartup;
+ private RecentsPackageMonitor mPackageMonitor;
+ private long mLastTabKeyEventTime;
+ private boolean mFinishedOnStartup;
+ private boolean mIgnoreAltTabRelease;
// Top level views
- RecentsView mRecentsView;
- SystemBarScrimViews mScrimViews;
- ViewStub mEmptyViewStub;
- View mEmptyView;
+ private RecentsView mRecentsView;
+ private SystemBarScrimViews mScrimViews;
+ private ViewStub mEmptyViewStub;
+ private View mEmptyView;
// Resize task debug
- RecentsResizeTaskDialog mResizeTaskDebugDialog;
+ private RecentsResizeTaskDialog mResizeTaskDebugDialog;
// Search AppWidget
- AppWidgetProviderInfo mSearchWidgetInfo;
- RecentsAppWidgetHost mAppWidgetHost;
- RecentsAppWidgetHostView mSearchWidgetHostView;
+ private AppWidgetProviderInfo mSearchWidgetInfo;
+ private RecentsAppWidgetHost mAppWidgetHost;
+ private RecentsAppWidgetHostView mSearchWidgetHostView;
// Runnables to finish the Recents activity
- FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ private FinishRecentsRunnable mFinishLaunchHomeRunnable;
// The trigger to automatically launch the current task
- DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
+ private DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
@Override
public void run() {
- boolean dismissed = dismissRecentsToFocusedTask(false);
+ dismissRecentsToFocusedTask();
}
});
/**
- * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
- * launching Home with some ActivityOptions. Generally we always launch home when we exit
- * Recents rather than just finishing the activity since we don't know what is behind Recents in
- * the task stack. The only case where we finish() directly is when we are cancelling the full
- * screen transition from the app.
+ * A common Runnable to finish Recents by launching Home with an animation depending on the
+ * last activity launch state. Generally we always launch home when we exit Recents rather than
+ * just finishing the activity since we don't know what is behind Recents in the task stack.
*/
class FinishRecentsRunnable implements Runnable {
Intent mLaunchIntent;
- ActivityOptions mLaunchOpts;
/**
- * Creates a finish runnable that starts the specified intent, using the given
- * ActivityOptions.
+ * Creates a finish runnable that starts the specified intent.
*/
- public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
+ public FinishRecentsRunnable(Intent launchIntent) {
mLaunchIntent = launchIntent;
- mLaunchOpts = opts;
}
@Override
public void run() {
try {
- startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
+ RecentsActivityLaunchState launchState =
+ Recents.getConfiguration().getLaunchState();
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
+ launchState.launchedFromSearchHome ?
+ R.anim.recents_to_search_launcher_enter :
+ R.anim.recents_to_launcher_enter,
+ launchState.launchedFromSearchHome ?
+ R.anim.recents_to_search_launcher_exit :
+ R.anim.recents_to_launcher_exit);
+ startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
}
@@ -189,18 +200,6 @@
mRecentsView.setTaskStack(stack);
}
- // Create the home intent runnable
- Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
- ActivityOptions.makeCustomAnimation(this,
- launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
- R.anim.recents_to_launcher_enter,
- launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
- R.anim.recents_to_launcher_exit));
-
// Mark the task that is the launch target
int launchTaskIndexInStack = 0;
if (launchState.launchedToTaskId != -1) {
@@ -222,14 +221,14 @@
mEmptyView = mEmptyViewStub.inflate();
}
mEmptyView.setVisibility(View.VISIBLE);
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
mRecentsView.setSearchBarVisibility(View.GONE);
}
} else {
if (mEmptyView != null) {
mEmptyView.setVisibility(View.GONE);
}
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
if (mRecentsView.hasValidSearchBar()) {
mRecentsView.setSearchBarVisibility(View.VISIBLE);
} else {
@@ -263,12 +262,9 @@
/**
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
- boolean dismissRecentsToFocusedTask(boolean checkFilteredStackState) {
+ boolean dismissRecentsToFocusedTask() {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
- // If we currently have filtered stacks, then unfilter those first
- if (checkFilteredStackState &&
- mRecentsView.unfilterFilteredStacks()) return true;
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
}
@@ -278,12 +274,9 @@
/**
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
- boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
+ boolean dismissRecentsToFocusedTaskOrHome() {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
- // If we currently have filtered stacks, then unfilter those first
- if (checkFilteredStackState &&
- mRecentsView.unfilterFilteredStacks()) return true;
// If we have a focused Task, launch that Task now
if (mRecentsView.launchFocusedTask()) return true;
// If none of the other cases apply, then just go Home
@@ -343,7 +336,7 @@
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
// Initialize the widget host (the host id is static and does not change)
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID);
}
mPackageMonitor = new RecentsPackageMonitor();
@@ -352,22 +345,30 @@
// Set the Recents layout
setContentView(R.layout.recents);
mRecentsView = (RecentsView) findViewById(R.id.recents_view);
- mRecentsView.setCallbacks(this);
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
mScrimViews = new SystemBarScrimViews(this);
+ getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+
+ // Create the home intent runnable
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
// Bind the search app widget when we first start up
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
}
// Register the broadcast receiver to handle messages when the screen is turned off
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
}
registerReceiver(mSystemBroadcastReceiver, filter);
@@ -394,7 +395,7 @@
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromAppWithThumbnail;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
- EventBus.getDefault().send(new EnterRecentsWindowAnimationStartedEvent());
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
if (!launchState.launchedHasConfigurationChanged) {
@@ -409,26 +410,17 @@
}
@Override
- protected void onResume() {
- super.onResume();
-
- final RecentsActivityLaunchState state = Recents.getConfiguration().getLaunchState();
- if (state.startHidden) {
- state.startHidden = false;
- mRecentsView.setStackViewVisibility(View.INVISIBLE);
- }
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@Override
protected void onPause() {
super.onPause();
- if (Constants.DebugFlags.App.EnableFastToggleRecents) {
- // Stop the fast-toggle dozer
- mIterateTrigger.stopDozing();
- }
-
- if (Constants.DebugFlags.App.EnableFastToggleRecents) {
+ RecentsDebugFlags flags = Recents.getDebugFlags();
+ if (flags.isFastToggleRecentsEnabled()) {
// Stop the fast-toggle dozer
mIterateTrigger.stopDozing();
}
@@ -438,6 +430,9 @@
protected void onStop() {
super.onStop();
+ // Reset some states
+ mIgnoreAltTabRelease = false;
+
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
@@ -453,6 +448,7 @@
launchState.launchedToTaskId = -1;
launchState.launchedWithAltTab = false;
launchState.launchedHasConfigurationChanged = false;
+ launchState.launchedViaDragGesture = false;
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
}
@@ -473,7 +469,7 @@
mPackageMonitor.unregister();
// Stop listening for widget package changes if there was one bound
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
mAppWidgetHost.stopListening();
}
@@ -508,10 +504,6 @@
boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
mLastTabKeyEventTime) > altTabKeyDelay;
if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
- // As we iterate to the next/previous task, cancel any current/lagging window
- // transition animations
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
-
// Focus the next task in the stack
final boolean backward = event.isShiftPressed();
if (backward) {
@@ -520,6 +512,11 @@
EventBus.getDefault().send(new FocusNextTaskViewEvent());
}
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
+
+ // In the case of another ALT event, don't ignore the next release
+ if (event.isAltPressed()) {
+ mIgnoreAltTabRelease = false;
+ }
}
return true;
}
@@ -554,7 +551,7 @@
@Override
public void onBackPressed() {
// Dismiss Recents to the focused Task or Home
- dismissRecentsToFocusedTaskOrHome(true);
+ dismissRecentsToFocusedTaskOrHome();
}
/**** RecentsResizeTaskDialog ****/
@@ -566,29 +563,25 @@
return mResizeTaskDebugDialog;
}
- /**** RecentsView.RecentsViewCallbacks Implementation ****/
-
- @Override
- public void onTaskLaunchFailed() {
- // Return to Home
- dismissRecentsToHome(true);
- }
-
- @Override
- public void onAllTaskViewsDismissed() {
- mFinishLaunchHomeRunnable.run();
- }
-
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
- dismissRecentsToFocusedTaskOrHome(true /* checkFilteredStackState */);
+ dismissRecentsToFocusedTaskOrHome();
}
public final void onBusEvent(IterateRecentsEvent event) {
// Focus the next task
EventBus.getDefault().send(new FocusNextTaskViewEvent());
- mIterateTrigger.poke();
+
+ // Start dozing after the recents button is clicked
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (debugFlags.isFastToggleRecentsEnabled()) {
+ if (!mIterateTrigger.isDozing()) {
+ mIterateTrigger.startDozing();
+ } else {
+ mIterateTrigger.poke();
+ }
+ }
}
public final void onBusEvent(UserInteractionEvent event) {
@@ -598,22 +591,24 @@
public final void onBusEvent(HideRecentsEvent event) {
if (event.triggeredFromAltTab) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
- dismissRecentsToFocusedTaskOrHome(false /* checkFilteredStackState */);
+ if (!mIgnoreAltTabRelease) {
+ dismissRecentsToFocusedTaskOrHome();
+ }
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
- dismissRecentsToHome(true /* checkFilteredStackState */);
+ dismissRecentsToHome(true /* animated */);
} else {
// Do nothing
}
}
- public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
ctx.postAnimationTrigger.increment();
if (mSearchWidgetInfo != null) {
- if (!Constants.DebugFlags.App.DisableSearchBar) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar) {
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
@@ -625,26 +620,22 @@
});
}
}
- ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- // If we are not launching with alt-tab and fast-toggle is enabled, then start
- // the dozer now
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (Constants.DebugFlags.App.EnableFastToggleRecents &&
- !launchState.launchedWithAltTab) {
- mIterateTrigger.startDozing();
- }
- }
- });
mRecentsView.startEnterRecentsAnimation(ctx);
ctx.postAnimationTrigger.decrement();
}
public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
- mRecentsView.setStackViewVisibility(View.VISIBLE);
+ EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+ mRecentsView.invalidate();
+ }
+
+ public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
+ if (mRecentsView.isLastTaskLaunchedFreeform()) {
+ EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(false));
+ }
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+ mRecentsView.invalidate();
}
public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
@@ -685,6 +676,14 @@
ssp.removeTask(event.task.key.id);
}
+ public final void onBusEvent(AllTaskViewsDismissedEvent event) {
+ // Just go straight home (no animation necessary because there are no more task views)
+ dismissRecentsToHome(false /* animated */);
+
+ // Keep track of all-deletions
+ MetricsLogger.count(this, "overview_task_all_dismissed", 1);
+ }
+
public final void onBusEvent(ResizeTaskEvent event) {
getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
}
@@ -701,10 +700,32 @@
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
+ public final void onBusEvent(LaunchTaskSucceededEvent event) {
+ MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
+ }
+
+ public final void onBusEvent(LaunchTaskFailedEvent event) {
+ // Return to Home
+ dismissRecentsToHome(true);
+
+ MetricsLogger.count(this, "overview_task_launch_failed", 1);
+ }
+
public final void onBusEvent(ScreenPinningRequestEvent event) {
MetricsLogger.count(this, "overview_screen_pinned", 1);
}
+ public final void onBusEvent(DebugFlagsChangedEvent event) {
+ // Just finish recents so that we can reload the flags anew on the next instantiation
+ finish();
+ }
+
+ public final void onBusEvent(StackViewScrolledEvent event) {
+ // Once the user has scrolled while holding alt-tab, then we should ignore the release of
+ // the key
+ mIgnoreAltTabRelease = true;
+ }
+
private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 01ffd2a..8dd9e47 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -34,7 +34,7 @@
public boolean launchedFromSearchHome;
public boolean launchedReuseTaskStackViews;
public boolean launchedHasConfigurationChanged;
- public boolean startHidden;
+ public boolean launchedViaDragGesture;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
@@ -46,6 +46,7 @@
launchedReuseTaskStackViews = false;
// Set this flag to indicate that the configuration has changed since Recents last launched
launchedHasConfigurationChanged = true;
+ launchedViaDragGesture = false;
}
/** Returns whether the status bar scrim should be animated when shown for the first time. */
@@ -74,7 +75,8 @@
* Returns the task to focus given the current launch state.
*/
public int getInitialFocusTaskIndex(int numTasks) {
- if (Constants.DebugFlags.App.EnableFastToggleRecents && !launchedWithAltTab) {
+ RecentsDebugFlags flags = Recents.getDebugFlags();
+ if (flags.isPageOnToggleEnabled() && !launchedWithAltTab) {
// If we are fast toggling, then focus the next task depending on when you are on home
// or coming in from another app
if (launchedFromHome) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index a73f323..440ed6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -154,7 +154,7 @@
int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
int top = searchBarBounds.isEmpty() ? topInset : 0;
taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom + top,
- windowBounds.right - swInset, windowBounds.bottom);
+ windowBounds.right - swInset - rightInset, windowBounds.bottom);
}
}
@@ -180,7 +180,8 @@
* Constrain the width of the landscape stack to the smallest width of the device.
*/
private int getInsetToSmallestWidth(int availableWidth) {
- if (availableWidth > smallestWidth) {
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (!debugFlags.isFullscreenThumbnailsEnabled() && (availableWidth > smallestWidth)) {
return (availableWidth - smallestWidth) / 2;
}
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
new file mode 100644
index 0000000..67d7115
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -0,0 +1,106 @@
+/*
+ * 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.recents;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
+import com.android.systemui.tuner.TunerService;
+
+/**
+ * Tunable debug flags
+ */
+public class RecentsDebugFlags implements TunerService.Tunable {
+
+ private static final String KEY_FAST_TOGGLE = "overview_fast_toggle";
+ private static final String KEY_PAGE_ON_TOGGLE = "overview_page_on_toggle";
+ private static final String KEY_FULLSCREEN_THUMBNAILS = "overview_fullscreen_thumbnails";
+
+ public static class Static {
+ // Enables debug drawing for the transition thumbnail
+ public static final boolean EnableTransitionThumbnailDebugMode = false;
+ // This disables the search bar integration
+ public static final boolean DisableSearchBar = true;
+ // This disables the bitmap and icon caches
+ public static final boolean DisableBackgroundCache = false;
+ // Enables the simulated task affiliations
+ public static final boolean EnableSimulatedTaskGroups = false;
+ // Defines the number of mock task affiliations per group
+ public static final int TaskAffiliationsGroupCount = 12;
+ // Enables us to create mock recents tasks
+ public static final boolean EnableSystemServicesProxy = false;
+ // Defines the number of mock recents packages to create
+ public static final int SystemServicesProxyMockPackageCount = 3;
+ // Defines the number of mock recents tasks to create
+ public static final int SystemServicesProxyMockTaskCount = 100;
+ }
+
+ private boolean mFastToggleRecents;
+ private boolean mPageOnToggle;
+ private boolean mUseFullscreenThumbnails;
+
+ /**
+ * We read the prefs once when we start the activity, then update them as the tuner changes
+ * the flags.
+ */
+ 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_PAGE_ON_TOGGLE,
+ KEY_FULLSCREEN_THUMBNAILS);
+ }
+
+ /**
+ * @return whether we are enabling fast toggling.
+ */
+ public boolean isFastToggleRecentsEnabled() {
+ return mPageOnToggle && mFastToggleRecents;
+ }
+
+ /**
+ * @return whether the recents button toggles pages.
+ */
+ public boolean isPageOnToggleEnabled() {
+ return mPageOnToggle;
+ }
+
+ /**
+ * @return whether we should show fullscreen thumbnails
+ */
+ public boolean isFullscreenThumbnailsEnabled() {
+ return mUseFullscreenThumbnails;
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ switch (key) {
+ case KEY_FAST_TOGGLE:
+ mFastToggleRecents = (newValue != null) &&
+ (Integer.parseInt(newValue) != 0);
+ break;
+ case KEY_PAGE_ON_TOGGLE:
+ mPageOnToggle = (newValue != null) &&
+ (Integer.parseInt(newValue) != 0);
+ break;
+ case KEY_FULLSCREEN_THUMBNAILS:
+ mUseFullscreenThumbnails = (newValue != null) &&
+ (Integer.parseInt(newValue) != 0);
+ break;
+ }
+ EventBus.getDefault().send(new DebugFlagsChangedEvent());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index db65e00..ee57863 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -35,18 +35,20 @@
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.View;
+
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
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.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -69,8 +71,8 @@
* 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.OnAnimationStartedListener, ActivityOptions.OnAnimationFinishedListener {
+public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements
+ ActivityOptions.OnAnimationFinishedListener {
private final static String TAG = "RecentsImpl";
private final static boolean DEBUG = false;
@@ -140,8 +142,9 @@
TaskStackListenerImpl mTaskStackListener;
RecentsAppWidgetHost mAppWidgetHost;
boolean mBootCompleted;
- boolean mStartAnimationTriggered;
boolean mCanReuseTaskStackViews = true;
+ boolean mDraggingInRecents;
+ boolean mReloadTasks;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -166,14 +169,14 @@
public void run() {
// When this fires, then the user has not released alt-tab for at least
// FAST_ALT_TAB_DELAY_MS milliseconds
- showRecents(mTriggeredFromAltTab);
+ showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
+ false /* reloadTasks */);
}
});
Bitmap mThumbnailTransitionBitmapCache;
Task mThumbnailTransitionBitmapCacheKey;
-
public RecentsImpl(Context context) {
mContext = context;
mHandler = new Handler();
@@ -250,8 +253,11 @@
}
@Override
- public void showRecents(boolean triggeredFromAltTab) {
+ public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
+ boolean animate, boolean reloadTasks) {
mTriggeredFromAltTab = triggeredFromAltTab;
+ mDraggingInRecents = draggingInRecents;
+ mReloadTasks = reloadTasks;
if (mFastAltTabTrigger.hasTriggered()) {
// We are calling this from the doze trigger, so just fall through to show Recents
mFastAltTabTrigger.resetTrigger();
@@ -282,7 +288,7 @@
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask == null || !ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
- startRecentsActivity(topTask, isTopTaskHome.value);
+ startRecentsActivity(topTask, isTopTaskHome.value, animate);
}
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to launch RecentsActivity", e);
@@ -317,6 +323,7 @@
return;
}
+ mDraggingInRecents = false;
mTriggeredFromAltTab = false;
try {
@@ -326,8 +333,8 @@
if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- if (Constants.DebugFlags.App.EnableFastToggleRecents &&
- !launchState.launchedWithAltTab) {
+ RecentsDebugFlags flags = Recents.getDebugFlags();
+ if (flags.isPageOnToggleEnabled() && !launchState.launchedWithAltTab) {
// Notify recents to move onto the next task
EventBus.getDefault().post(new IterateRecentsEvent());
} else {
@@ -353,7 +360,7 @@
}
// Otherwise, start the recents activity
- startRecentsActivity(topTask, isTopTaskHome.value);
+ startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */);
mLastToggleTime = SystemClock.elapsedRealtime();
}
} catch (ActivityNotFoundException e) {
@@ -387,6 +394,16 @@
// Do nothing
}
+ @Override
+ public void onDraggingInRecents(float distanceFromTop) {
+ EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
+ }
+
+ @Override
+ public void onDraggingInRecentsEnded(float velocity) {
+ EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
+ }
+
/**
* Transitions to the next recent task in the stack.
*/
@@ -403,17 +420,22 @@
ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
// Return early if there is no running task
if (runningTask == null) return;
- // Return early if the running task is in the home stack (optimization)
- if (SystemServicesProxy.isHomeStack(runningTask.stackId)) return;
// Find the task in the recents list
+ boolean isTopTaskHome = SystemServicesProxy.isHomeStack(runningTask.stackId);
ArrayList<Task> tasks = focusedStack.getTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 1; i--) {
Task task = tasks.get(i);
- if (task.key.id == runningTask.id) {
+ if (isTopTaskHome) {
+ toTask = tasks.get(i - 1);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_target,
+ R.anim.recents_fast_toggle_app_home_exit);
+ break;
+ } else if (task.key.id == runningTask.id) {
toTask = tasks.get(i - 1);
launchOpts = ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_launch_prev_affiliated_task_target,
@@ -503,12 +525,7 @@
MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
// Launch the task
- if (toTask.isActive) {
- // Bring an active task to the foreground
- ssp.moveTaskToFront(toTask.key.id, launchOpts);
- } else {
- ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
- }
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
}
public void showNextAffiliatedTask() {
@@ -523,13 +540,14 @@
showRelativeAffiliatedTask(false);
}
- public void dockTopTask() {
+ public void dockTopTask(boolean draggingInRecents, Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
- ssp.startTaskInDockedMode(topTask.id,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
- showRecents(false /* triggeredFromAltTab */);
+ ssp.moveTaskToDockedStack(topTask.id,
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, initialBounds);
+ showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */,
+ true /* reloadTasks*/);
}
}
@@ -557,7 +575,7 @@
// Update the configuration for the current state
config.update(mContext, ssp, ssp.getWindowRect());
- if (!Constants.DebugFlags.App.DisableSearchBar && tryAndBindSearchWidget) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar && tryAndBindSearchWidget) {
// Try and pre-emptively bind the search widget on startup to ensure that we
// have the right thumbnail bounds to animate to.
// Note: We have to reload the widget id before we get the task stack bounds below
@@ -618,7 +636,7 @@
final Task toTask = new Task();
final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
topTask.id, toTask);
- ForegroundThread.getHandler().post(new Runnable() {
+ ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
final Bitmap transitionBitmap = drawThumbnailTransitionBitmap(toTask, toTransform);
@@ -640,7 +658,7 @@
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit,
- mHandler, this);
+ mHandler, null);
}
/**
@@ -651,12 +669,12 @@
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_search_launcher_enter,
R.anim.recents_from_search_launcher_exit,
- mHandler, this);
+ mHandler, null);
}
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit,
- mHandler, this);
+ mHandler, null);
}
/**
@@ -682,7 +700,7 @@
AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
specs.toArray(specsArray);
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- specsArray, mHandler, this, this);
+ specsArray, mHandler, null, this);
} else {
// Update the destination rect
Task toTask = new Task();
@@ -693,7 +711,7 @@
if (thumbnail != null) {
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
- (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, this);
+ (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
}
// If both the screenshot and thumbnail fails, then just fall back to the default transition
return getUnknownTransitionActivityOptions();
@@ -760,7 +778,7 @@
int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
Bitmap.Config.ARGB_8888);
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
thumbnail.eraseColor(0xFFff0000);
} else {
Canvas c = new Canvas(thumbnail);
@@ -779,18 +797,20 @@
* Shows the recents activity
*/
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- boolean isTopTaskHome) {
+ boolean isTopTaskHome, boolean animate) {
RecentsTaskLoader loader = Recents.getTaskLoader();
// Update the header bar if necessary
reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
- if (sInstanceLoadPlan == null) {
- // Create a new load plan if onPreloadRecents() was never triggered
+ // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
+ // should always preload the tasks now. If we are dragging in recents, reload them as
+ // the stacks might have changed.
+ if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ // Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
-
- if (!sInstanceLoadPlan.hasTasks()) {
+ if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -799,6 +819,14 @@
mDummyStackView.updateLayoutForStack(stack);
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
+
+ if (!animate) {
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
+ startRecentsActivity(topTask, opts, false /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail*/, stackVr);
+ return;
+ }
+
boolean hasRecentTasks = stack.getTaskCount() > 0;
boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
@@ -818,11 +846,11 @@
if (!useThumbnailTransition) {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition and do the animation from home
- if (!Constants.DebugFlags.App.DisableSearchBar && hasRecentTasks) {
+ if (!RecentsDebugFlags.Static.DisableSearchBar && hasRecentTasks) {
SystemServicesProxy ssp = Recents.getSystemServices();
String homeActivityPackage = ssp.getHomeActivityPackageName();
String searchWidgetPackage = Prefs.getString(mContext,
- Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
+ Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
// Determine whether we are coming from a search owned home activity
boolean fromSearchHome = (homeActivityPackage != null) &&
@@ -846,8 +874,6 @@
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
TaskStackLayoutAlgorithm.VisibilityReport vr) {
- mStartAnimationTriggered = false;
-
// Update the configuration based on the launch options
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
@@ -860,7 +886,7 @@
launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
launchState.launchedHasConfigurationChanged = false;
- launchState.startHidden = topTask != null && topTask.stackId == FREEFORM_WORKSPACE_STACK_ID;
+ launchState.launchedViaDragGesture = mDraggingInRecents;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
@@ -875,16 +901,7 @@
mCanReuseTaskStackViews = true;
}
- /**** OnAnimationStartedListener Implementation ****/
-
- @Override
- public void onAnimationStarted() {
- // Notify recents to start the enter animation
- if (!mStartAnimationTriggered) {
- mStartAnimationTriggered = true;
- EventBus.getDefault().post(new EnterRecentsWindowAnimationStartedEvent());
- }
- }
+ /**** OnAnimationFinishedListener Implementation ****/
@Override
public void onAnimationFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index b091f05..eb81e80 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -502,6 +502,19 @@
queueEvent(event);
}
+ /**
+ * If this method is called from the main thread, it will be handled directly. If this method
+ * is not called from the main thread, it will be posted onto the main thread.
+ */
+ public void sendOntoMainThread(Event event) {
+ long callingThreadId = Thread.currentThread().getId();
+ if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+ post(event);
+ } else {
+ send(event);
+ }
+ }
+
/** Prevent post()ing an InterprocessEvent */
@Deprecated
public void post(InterprocessEvent event) {
@@ -748,7 +761,7 @@
} catch (IllegalAccessException e) {
Log.e(TAG, "Failed to invoke method", e.getCause());
} catch (InvocationTargetException e) {
- Log.e(TAG, "Failed to invoke method", e.getCause());
+ throw new RuntimeException("Failed to invoke method", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java
index f187178..fe3bf26 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DebugFlagsChangedEvent.java
@@ -19,8 +19,8 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent when the SystemUI tuner changes a flag.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+public class DebugFlagsChangedEvent extends EventBus.Event {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
index f187178..b31f320 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
@@ -19,8 +19,10 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent when the window animation into Recents completes. We use this signal to know when
+ * we can start in-app animations so that they don't conflict with the window transition into
+ * Recents.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+public class EnterRecentsWindowAnimationCompletedEvent extends EventBus.Event {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
similarity index 84%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
index f187178..3a2d58c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
@@ -19,8 +19,8 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent when we fail to launch a task.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+public class LaunchTaskFailedEvent extends EventBus.Event {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
index f187178..ec5089f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
@@ -19,8 +19,13 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent when we successfully launch a task.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+public class LaunchTaskSucceededEvent extends EventBus.Event {
+
+ public final int taskIndexFromStackFront;
+
+ public LaunchTaskSucceededEvent(int taskIndexFromStackFront) {
+ this.taskIndexFromStackFront = taskIndexFromStackFront;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
index 5cb4ccf..f9ccfc8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -18,7 +18,6 @@
import android.content.Context;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.SystemServicesProxy;
/**
* This is sent when we want to start screen pinning.
@@ -26,10 +25,8 @@
public class ScreenPinningRequestEvent extends EventBus.Event {
public final Context applicationContext;
- public final SystemServicesProxy systemServicesProxy;
- public ScreenPinningRequestEvent(Context context, SystemServicesProxy systemServicesProxy) {
+ public ScreenPinningRequestEvent(Context context) {
this.applicationContext = context.getApplicationContext();
- this.systemServicesProxy = systemServicesProxy;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
similarity index 77%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
index f187178..cf74519 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent whenever all the task views in a stack have been dismissed.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+public class AllTaskViewsDismissedEvent extends EventBus.Event {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
new file mode 100644
index 0000000..9be8eb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
@@ -0,0 +1,15 @@
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus.Event;
+
+/**
+ * This event is sent when the user finished dragging in recents.
+ */
+public class DraggingInRecentsEndedEvent extends Event {
+
+ public final float velocity;
+
+ public DraggingInRecentsEndedEvent(float velocity) {
+ this.velocity = velocity;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
new file mode 100644
index 0000000..5e8bfd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
@@ -0,0 +1,15 @@
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus.Event;
+
+/**
+ * This event is sent when the user changed how far they are dragging in recents.
+ */
+public class DraggingInRecentsEvent extends Event {
+
+ public final float distanceFromTop;
+
+ public DraggingInRecentsEvent(float distanceFromTop) {
+ this.distanceFromTop = distanceFromTop;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
index f187178..cb5011a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
@@ -14,13 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent whenever a new scroll gesture happens on a stack view.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+public class StackViewScrolledEvent extends EventBus.Event {
+
+ public final int yMovement;
+
+ public StackViewScrolledEvent(int yMovement) {
+ this.yMovement = yMovement;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java
index f187178..b42da9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateFreeformTaskViewVisibilityEvent.java
@@ -14,13 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.activity;
+package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent to update the visibility of all visible freeform task views.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
- // Simple event
+public class UpdateFreeformTaskViewVisibilityEvent extends EventBus.Event {
+
+ public final boolean visible;
+
+ public UpdateFreeformTaskViewVisibilityEvent(boolean visible) {
+ this.visible = visible;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
index 8dc2983..784ac4e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
@@ -28,7 +28,7 @@
private static Handler sHandler;
private ForegroundThread() {
- super("recents.fg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ super("recents.fg");
}
private static void ensureThreadLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/FreePathInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/misc/FreePathInterpolator.java
new file mode 100644
index 0000000..720c952
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/FreePathInterpolator.java
@@ -0,0 +1,177 @@
+/*
+ * 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.recents.misc;
+
+import android.graphics.Path;
+import android.view.animation.BaseInterpolator;
+import android.view.animation.Interpolator;
+
+/**
+ * An interpolator that can traverse a Path. The x coordinate along the <code>Path</code>
+ * is the input value and the output is the y coordinate of the line at that point.
+ * This means that the Path must conform to a function <code>y = f(x)</code>.
+ *
+ * <p>The <code>Path</code> must not have gaps in the x direction and must not
+ * loop back on itself such that there can be two points sharing the same x coordinate.
+ * It is alright to have a disjoint line in the vertical direction:</p>
+ * <p><blockquote><pre>
+ * Path path = new Path();
+ * path.lineTo(0.25f, 0.25f);
+ * path.moveTo(0.25f, 0.5f);
+ * path.lineTo(1f, 1f);
+ * </pre></blockquote></p>
+ */
+public class FreePathInterpolator extends BaseInterpolator {
+
+ // This governs how accurate the approximation of the Path is.
+ private static final float PRECISION = 0.002f;
+
+ private float[] mX;
+ private float[] mY;
+ private float mArcLength;
+
+ /**
+ * Create an interpolator for an arbitrary <code>Path</code>.
+ *
+ * @param path The <code>Path</code> to use to make the line representing the interpolator.
+ */
+ public FreePathInterpolator(Path path) {
+ initPath(path);
+ }
+
+ private void initPath(Path path) {
+ float[] pointComponents = path.approximate(PRECISION);
+
+ int numPoints = pointComponents.length / 3;
+
+ mX = new float[numPoints];
+ mY = new float[numPoints];
+ mArcLength = 0;
+ float prevX = 0;
+ float prevY = 0;
+ float prevFraction = 0;
+ int componentIndex = 0;
+ for (int i = 0; i < numPoints; i++) {
+ float fraction = pointComponents[componentIndex++];
+ float x = pointComponents[componentIndex++];
+ float y = pointComponents[componentIndex++];
+ if (fraction == prevFraction && x != prevX) {
+ throw new IllegalArgumentException(
+ "The Path cannot have discontinuity in the X axis.");
+ }
+ if (x < prevX) {
+ throw new IllegalArgumentException("The Path cannot loop back on itself.");
+ }
+ mX[i] = x;
+ mY[i] = y;
+ mArcLength += Math.hypot(x - prevX, y - prevY);
+ prevX = x;
+ prevY = y;
+ prevFraction = fraction;
+ }
+ }
+
+ /**
+ * Using the line in the Path in this interpolator that can be described as
+ * <code>y = f(x)</code>, finds the y coordinate of the line given <code>t</code>
+ * as the x coordinate.
+ *
+ * @param t Treated as the x coordinate along the line.
+ * @return The y coordinate of the Path along the line where x = <code>t</code>.
+ * @see Interpolator#getInterpolation(float)
+ */
+ @Override
+ public float getInterpolation(float t) {
+ int startIndex = 0;
+ int endIndex = mX.length - 1;
+
+ // Return early if out of bounds
+ if (t <= 0) {
+ return mY[startIndex];
+ } else if (t >= 1) {
+ return mY[endIndex];
+ }
+
+ // Do a binary search for the correct x to interpolate between.
+ while (endIndex - startIndex > 1) {
+ int midIndex = (startIndex + endIndex) / 2;
+ if (t < mX[midIndex]) {
+ endIndex = midIndex;
+ } else {
+ startIndex = midIndex;
+ }
+ }
+
+ float xRange = mX[endIndex] - mX[startIndex];
+ if (xRange == 0) {
+ return mY[startIndex];
+ }
+
+ float tInRange = t - mX[startIndex];
+ float fraction = tInRange / xRange;
+
+ float startY = mY[startIndex];
+ float endY = mY[endIndex];
+ return startY + (fraction * (endY - startY));
+ }
+
+ /**
+ * Finds the x that provides the given <code>y = f(x)</code>.
+ *
+ * @param y a value from (0,1) that is in this path.
+ */
+ public float getX(float y) {
+ int startIndex = 0;
+ int endIndex = mY.length - 1;
+
+ // Return early if out of bounds
+ if (y <= 0) {
+ return mX[endIndex];
+ } else if (y >= 1) {
+ return mX[startIndex];
+ }
+
+ // Do a binary search for index that bounds the y
+ while (endIndex - startIndex > 1) {
+ int midIndex = (startIndex + endIndex) / 2;
+ if (y < mY[midIndex]) {
+ startIndex = midIndex;
+ } else {
+ endIndex = midIndex;
+ }
+ }
+
+ float yRange = mY[endIndex] - mY[startIndex];
+ if (yRange == 0) {
+ return mX[startIndex];
+ }
+
+ float tInRange = y - mY[startIndex];
+ float fraction = tInRange / yRange;
+
+ float startX = mX[startIndex];
+ float endX = mX[endIndex];
+ return startX + (fraction * (endX - startX));
+ }
+
+ /**
+ * Returns the arclength of the path we are interpolating.
+ */
+ public float getArcLength() {
+ return mArcLength;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
deleted file mode 100644
index 515c3bd..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
+++ /dev/null
@@ -1,274 +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.recents.misc;
-
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Log;
-
-/**
- * Represents a 2d curve that is parameterized along the arc length of the curve (p), and allows the
- * conversions of x->p and p->x.
- */
-public class ParametricCurve {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "ParametricCurve";
-
- private static final int PrecisionSteps = 250;
-
- /**
- * A 2d function, representing the curve.
- */
- public interface CurveFunction {
- float f(float x);
- float invF(float y);
- }
-
- /**
- * A function that returns a value given a parametric value.
- */
- public interface ParametricCurveFunction {
- float f(float p);
- }
-
- float[] xp;
- float[] px;
- float mLength;
-
- CurveFunction mFn;
- ParametricCurveFunction mScaleFn;
-
- public ParametricCurve(CurveFunction fn, ParametricCurveFunction scaleFn) {
- long t1;
- if (DEBUG) {
- t1 = SystemClock.currentThreadTimeMicro();
- Log.d(TAG, "initializeCurve");
- }
- mFn = fn;
- mScaleFn = scaleFn;
- xp = new float[PrecisionSteps + 1];
- px = new float[PrecisionSteps + 1];
-
- // Approximate f(x)
- float[] fx = new float[PrecisionSteps + 1];
- float step = 1f / PrecisionSteps;
- float x = 0;
- for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
- fx[xStep] = fn.f(x);
- x += step;
- }
- // Calculate the arc length for x:1->0
- float pLength = 0;
- float[] dx = new float[PrecisionSteps + 1];
- dx[0] = 0;
- for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
- dx[xStep] = (float) Math.hypot(fx[xStep] - fx[xStep - 1], step);
- pLength += dx[xStep];
- }
- mLength = pLength;
- // Approximate p(x), a function of cumulative progress with x, normalized to 0..1
- float p = 0;
- px[0] = 0f;
- px[PrecisionSteps] = 1f;
- if (DEBUG) {
- Log.d(TAG, "p[0]=0");
- Log.d(TAG, "p[" + PrecisionSteps + "]=1");
- }
- for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
- p += Math.abs(dx[xStep] / pLength);
- px[xStep] = p;
- if (DEBUG) {
- Log.d(TAG, "p[" + xStep + "]=" + p);
- }
- }
- // Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
- // function.
- int xStep = 0;
- p = 0;
- xp[0] = 0f;
- xp[PrecisionSteps] = 1f;
- if (DEBUG) {
- Log.d(TAG, "x[0]=0");
- Log.d(TAG, "x[" + PrecisionSteps + "]=1");
- }
- for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
- // Walk forward in px and find the x where px <= p && p < px+1
- while (xStep < PrecisionSteps) {
- if (px[xStep] > p) break;
- xStep++;
- }
- // Now, px[xStep-1] <= p < px[xStep]
- if (xStep == 0) {
- xp[pStep] = 0;
- } else {
- // Find x such that proportionally, x is correct
- float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
- x = (xStep - 1 + fraction) * step;
- xp[pStep] = x;
- }
- if (DEBUG) {
- Log.d(TAG, "x[" + pStep + "]=" + xp[pStep]);
- }
- p += step;
- }
- if (DEBUG) {
- Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
- }
- }
-
- /**
- * Converts from the progress along the arc-length of the curve to a coordinate within the
- * bounds. Note, p=0 represents the top of the bounds, and p=1 represents the bottom.
- */
- public int pToX(float p, Rect bounds) {
- int top = bounds.top;
- int height = bounds.height();
-
- if (p <= 0f) return top;
- if (p >= 1f) return top + (int) (p * height);
-
- float pIndex = p * PrecisionSteps;
- int pFloorIndex = (int) Math.floor(pIndex);
- int pCeilIndex = (int) Math.ceil(pIndex);
- float x = xp[pFloorIndex];
- if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
- // Interpolate between the two precalculated positions
- x += (xp[pCeilIndex] - xp[pFloorIndex]) * (pIndex - pFloorIndex);
- }
- return top + (int) (x * height);
- }
-
- /**
- * Converts from the progress along the arc-length of the curve to a scale.
- */
- public float pToScale(float p) {
- return mScaleFn.f(p);
- }
-
- /**
- * Converts from a bounds coordinate to the progress along the arc-length of the curve.
- * Note, p=0 represents the top of the bounds, and p=1 represents the bottom.
- */
- public float xToP(int x, Rect bounds) {
- int top = bounds.top;
-
- float xf = (float) (x - top) / bounds.height();
- if (xf <= 0f) return 0f;
- if (xf >= 1f) return xf;
-
- float xIndex = xf * PrecisionSteps;
- int xFloorIndex = (int) Math.floor(xIndex);
- int xCeilIndex = (int) Math.ceil(xIndex);
- float p = px[xFloorIndex];
- if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
- // Interpolate between the two precalculated positions
- p += (px[xCeilIndex] - px[xFloorIndex]) * (xIndex - xFloorIndex);
- }
- return p;
- }
-
- /**
- * Computes the progress offset from the bottom of the curve (p=1) such that the given height
- * is visible when scaled at the that progress.
- */
- public float computePOffsetForScaledHeight(int height, Rect bounds) {
- int top = bounds.top;
- int bottom = bounds.bottom;
- height = Math.min(height, bottom - top);
-
- if (bounds.height() == 0) {
- return 0;
- }
-
- // Find the next p(x) such that (bottom-x) == scale(p(x))*height
- int minX = top;
- int maxX = bottom;
- long t1;
- if (DEBUG) {
- t1 = SystemClock.currentThreadTimeMicro();
- Log.d(TAG, "computePOffsetForScaledHeight: " + height);
- }
- while (minX <= maxX) {
- int midX = minX + ((maxX - minX) / 2);
- float pMidX = xToP(midX, bounds);
- float scaleMidX = mScaleFn.f(pMidX);
- int scaledHeight = (int) (scaleMidX * height);
- if ((bottom - midX) < scaledHeight) {
- maxX = midX - 1;
- } else if ((bottom - midX) > scaledHeight) {
- minX = midX + 1;
- } else {
- if (DEBUG) {
- Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
- }
- return 1f - pMidX;
- }
- }
- if (DEBUG) {
- Log.d(TAG, "\t2t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
- }
- return 1f - xToP(maxX, bounds);
- }
-
- /**
- * Computes the progress offset from the bottom of the curve (p=1) that allows the given height,
- * unscaled at the progress, will be visible.
- */
- public float computePOffsetForHeight(int height, Rect bounds) {
- int top = bounds.top;
- int bottom = bounds.bottom;
- height = Math.min(height, bottom - top);
-
- if (bounds.height() == 0) {
- return 0;
- }
-
- // Find the next p(x) such that (bottom-x) == height
- int minX = top;
- int maxX = bottom;
- long t1;
- if (DEBUG) {
- t1 = SystemClock.currentThreadTimeMicro();
- Log.d(TAG, "computePOffsetForHeight: " + height);
- }
- while (minX <= maxX) {
- int midX = minX + ((maxX - minX) / 2);
- if ((bottom - midX) < height) {
- maxX = midX - 1;
- } else if ((bottom - midX) > height) {
- minX = midX + 1;
- } else {
- if (DEBUG) {
- Log.d(TAG, "\t1t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
- }
- return 1f - xToP(midX, bounds);
- }
- }
- if (DEBUG) {
- Log.d(TAG, "\t2t: " + (SystemClock.currentThreadTimeMicro() - t1) + "microsecs");
- }
- return 1f - xToP(maxX, bounds);
- }
-
- /**
- * Returns the length of this curve.
- */
- public float getArcLength() {
- return mLength;
- }
-}
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 0432ac9..de40a37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -63,7 +63,8 @@
import com.android.internal.os.BackgroundThread;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
import java.io.IOException;
@@ -113,6 +114,7 @@
/** Private constructor */
public SystemServicesProxy(Context context) {
+ RecentsDebugFlags flags = Recents.getDebugFlags();
mAccm = AccessibilityManager.getInstance(context);
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIam = ActivityManagerNative.getDefault();
@@ -124,8 +126,8 @@
mUm = UserManager.get(context);
mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
- mHasFreeformWorkspaceSupport = false && mPm.hasSystemFeature(
- PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+ mHasFreeformWorkspaceSupport = false &&
+ mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -143,7 +145,7 @@
// Resolve the assist intent
mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
// Create a dummy icon
mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
mDummyIcon.eraseColor(0xFF999999);
@@ -156,13 +158,13 @@
if (mAm == null) return null;
// If we are mocking, then create some recent tasks
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
ArrayList<ActivityManager.RecentTaskInfo> tasks =
new ArrayList<ActivityManager.RecentTaskInfo>();
- int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
+ int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.SystemServicesProxyMockTaskCount);
for (int i = 0; i < count; i++) {
// Create a dummy component name
- int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
+ int packageIndex = i % RecentsDebugFlags.Static.SystemServicesProxyMockPackageCount;
ComponentName cn = new ComponentName("com.android.test" + packageIndex,
"com.android.test" + i + ".Activity");
String description = "" + i + " - " +
@@ -309,6 +311,18 @@
}
}
+ /** Docks an already resumed task to the side of the screen. */
+ public void moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
+ if (mIam == null) return;
+
+ try {
+ mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */, false /* animate */,
+ initialBounds);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the focused stack id. */
public int getFocusedStack() {
if (mIam == null) return -1;
@@ -329,6 +343,13 @@
}
/**
+ * Returns whether the given stack id is the docked stack id.
+ */
+ public static boolean isDockedStack(int stackId) {
+ return stackId == DOCKED_STACK_ID;
+ }
+
+ /**
* Returns whether the given stack id is the freeform workspace stack id.
*/
public static boolean isFreeformStack(int stackId) {
@@ -381,7 +402,7 @@
if (mAm == null) return null;
// If we are mocking, then just return a dummy thumbnail
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
Bitmap.Config.ARGB_8888);
thumbnail.eraseColor(0xff333333);
@@ -443,7 +464,7 @@
/** Moves a task to the front with the specified activity options. */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
if (opts != null) {
mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
@@ -456,7 +477,7 @@
/** Removes the task */
public void removeTask(final int taskId) {
if (mAm == null) return;
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
// Remove the task.
BackgroundThread.getHandler().post(new Runnable() {
@@ -475,7 +496,7 @@
*/
public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
if (mIpm == null) return null;
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
try {
return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
@@ -492,7 +513,7 @@
*/
public ActivityInfo getActivityInfo(ComponentName cn) {
if (mPm == null) return null;
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
try {
return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
@@ -507,7 +528,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
return "Recent Task";
}
@@ -519,7 +540,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
return "Recent Task";
}
@@ -549,7 +570,7 @@
if (mPm == null) return null;
// If we are mocking, then return a mock label
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
return new ColorDrawable(0xFF666666);
}
@@ -580,7 +601,7 @@
/** Returns the package name of the home activity. */
public String getHomeActivityPackageName() {
if (mPm == null) return null;
- if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null;
ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
@@ -623,22 +644,22 @@
* Returns the current search widget id.
*/
public int getSearchAppWidgetId(Context context) {
- return Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
+ return Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
}
/**
* Returns the current search widget info, binding a new one if necessary.
*/
public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
- int searchWidgetId = Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
+ int searchWidgetId = Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
// Return the search widget info if it hasn't changed
if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
- if (Prefs.getString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null) == null) {
- Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
+ if (Prefs.getString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null) == null) {
+ Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
searchWidgetInfo.provider.getPackageName());
}
return searchWidgetInfo;
@@ -654,16 +675,16 @@
Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
resolvedSearchWidgetInfo);
if (widgetInfo != null) {
- Prefs.putInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, widgetInfo.first);
- Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
+ Prefs.putInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, widgetInfo.first);
+ Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
widgetInfo.second.provider.getPackageName());
return widgetInfo.second;
}
}
// If we fall through here, then there is no resolved search widget, so clear the state
- Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_ID);
- Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE);
+ Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID);
+ Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 62493d6..7b04493 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -130,10 +130,9 @@
? t.taskDescription.getIconFilename() : null;
// Add the task to the stack
- Task task = new Task(taskKey, (t.id != INVALID_TASK_ID), t.affiliatedTaskId,
- t.affiliatedTaskColor, activityLabel, contentDescription, activityIcon,
- activityColor, (i == (taskCount - 1)), config.lockToAppEnabled, icon,
- iconFilename, t.bounds);
+ Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel,
+ contentDescription, activityIcon, activityColor, (i == (taskCount - 1)),
+ config.lockToAppEnabled, icon, iconFilename, t.bounds);
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
if (DEBUG) {
Log.d(TAG, activityLabel + " bounds: " + t.bounds);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index bba453a..965e7a67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -30,9 +30,9 @@
import android.util.Log;
import android.util.LruCache;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -239,7 +239,8 @@
SystemServicesProxy ssp, Resources res) {
Bitmap tdIcon = iconBitmap != null
? iconBitmap
- : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename);
+ : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename,
+ taskKey.userId);
if (tdIcon != null) {
return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
}
@@ -283,9 +284,9 @@
res.getColor(R.color.recents_task_bar_default_background_color);
mMaxThumbnailCacheSize = res.getInteger(R.integer.config_recents_max_thumbnail_count);
mMaxIconCacheSize = res.getInteger(R.integer.config_recents_max_icon_count);
- int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
+ int iconCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
mMaxIconCacheSize;
- int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
+ int thumbnailCacheSize = RecentsDebugFlags.Static.DisableBackgroundCache ? 1 :
mMaxThumbnailCacheSize;
// Create the default assets
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 12bd556b..67e18f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -103,7 +103,6 @@
public int colorPrimary;
public boolean useLightOnPrimaryColor;
public Bitmap thumbnail;
- public boolean isActive;
public boolean lockToThisTask;
public boolean lockToTaskEnabled;
public Bitmap icon;
@@ -116,7 +115,7 @@
// Do nothing
}
- public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
+ public Task(TaskKey key, int taskAffiliation, int taskAffiliationColor,
String activityTitle, String contentDescription, Drawable activityIcon,
int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
String iconFilename, Rect bounds) {
@@ -131,7 +130,6 @@
this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
- this.isActive = isActive;
this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
this.lockToTaskEnabled = lockToTaskEnabled;
this.icon = icon;
@@ -149,7 +147,6 @@
this.activityIcon = o.activityIcon;
this.colorPrimary = o.colorPrimary;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
- this.isActive = o.isActive;
this.lockToThisTask = o.lockToThisTask;
this.lockToTaskEnabled = o.lockToTaskEnabled;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 6734012..0970252 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -24,8 +24,8 @@
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -59,7 +59,7 @@
class FilteredTaskList {
private static final String TAG = "FilteredTaskList";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
ArrayList<Task> mTasks = new ArrayList<>();
ArrayList<Task> mFilteredTasks = new ArrayList<>();
@@ -74,18 +74,17 @@
if (!prevFilteredTasks.equals(mFilteredTasks)) {
return true;
} else {
- // If the tasks are exactly the same pre/post filter, then just reset it
- mFilter = null;
return false;
}
}
- /** Resets this FilteredTaskList. */
+ /**
+ * Resets the task list, but does not remove the filter.
+ */
void reset() {
mTasks.clear();
mFilteredTasks.clear();
mTaskIndices.clear();
- mFilter = null;
}
/** Removes the task filter and returns the previous touch state */
@@ -200,16 +199,10 @@
/** Task stack callbacks */
public interface TaskStackCallbacks {
/* Notifies when a task has been added to the stack */
- public void onStackTaskAdded(TaskStack stack, Task t);
+ void onStackTaskAdded(TaskStack stack, Task t);
/* Notifies when a task has been removed from the stack */
- public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
+ void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Task newFrontMostTask);
- /* Notifies when all task has been removed from the stack */
- public void onStackAllTasksRemoved(TaskStack stack, ArrayList<Task> removedTasks);
- /** Notifies when the stack was filtered */
- public void onStackFiltered(TaskStack newStack, ArrayList<Task> curTasks, Task t);
- /** Notifies when the stack was un-filtered */
- public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
@@ -300,8 +293,18 @@
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
- ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
- HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
+ ArrayList<TaskGrouping> mGroups = new ArrayList<>();
+ HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<>();
+
+ public TaskStack() {
+ // Ensure that we only show non-docked tasks
+ mTaskList.setFilter(new TaskFilter() {
+ @Override
+ public boolean acceptTask(Task t, int index) {
+ return !SystemServicesProxy.isDockedStack(t.key.stackId);
+ }
+ });
+ }
/** Sets the callbacks for this task stack. */
public void setCallbacks(TaskStackCallbacks cb) {
@@ -377,20 +380,6 @@
}
}
- /** Removes all tasks */
- public void removeAllTasks() {
- ArrayList<Task> taskList = new ArrayList<Task>(mTaskList.getTasks());
- int taskCount = taskList.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task t = taskList.get(i);
- removeTaskImpl(t);
- }
- if (mCb != null) {
- // Notify that all tasks have been removed
- mCb.onStackAllTasksRemoved(this, taskList);
- }
- }
-
/** Sets a few tasks in one go */
public void setTasks(List<Task> tasks) {
ArrayList<Task> taskList = mTaskList.getTasks();
@@ -419,7 +408,7 @@
/** Gets the task keys */
public ArrayList<Task.TaskKey> getTaskKeys() {
- ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
+ ArrayList<Task.TaskKey> taskKeys = new ArrayList<>();
ArrayList<Task> tasks = mTaskList.getTasks();
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
@@ -486,41 +475,6 @@
return false;
}
- /******** Filtering ********/
-
- /** Filters the stack into tasks similar to the one specified */
- public void filterTasks(final Task t) {
- ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks());
-
- // Set the task list filter
- boolean filtered = mTaskList.setFilter(new TaskFilter() {
- @Override
- public boolean acceptTask(Task at, int i) {
- return t.key.getComponent().getPackageName().equals(
- at.key.getComponent().getPackageName());
- }
- });
- if (filtered && mCb != null) {
- mCb.onStackFiltered(this, oldStack, t);
- }
- }
-
- /** Unfilters the current stack */
- public void unfilterTasks() {
- ArrayList<Task> oldStack = new ArrayList<Task>(mTaskList.getTasks());
-
- // Unset the filter, then update the virtual scroll
- mTaskList.removeFilter();
- if (mCb != null) {
- mCb.onStackUnfiltered(this, oldStack);
- }
- }
-
- /** Returns whether tasks are currently filtered */
- public boolean hasFilteredTasks() {
- return mTaskList.hasFilter();
- }
-
/******** Grouping ********/
/** Adds a group to the set */
@@ -543,7 +497,7 @@
* Temporary: This method will simulate affiliation groups by
*/
public void createAffiliatedGroupings(Context context) {
- if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
+ if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) {
HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
// Sort all tasks by increasing firstActiveTime of the task
ArrayList<Task> tasks = mTaskList.getTasks();
@@ -559,7 +513,7 @@
String prevPackage = "";
int prevAffiliation = -1;
Random r = new Random();
- int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
+ int groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
String packageName = t.key.getComponent().getPackageName();
@@ -574,7 +528,7 @@
addGroup(group);
prevAffiliation = affiliation;
prevPackage = packageName;
- groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
+ groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
}
group.addTask(t);
taskMap.put(t.key, t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
new file mode 100644
index 0000000..d7eb099
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -0,0 +1,340 @@
+/*
+ * 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.recents.views;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.StackId;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.recents.ExitRecentsWindowFirstAnimationFrameEvent;
+import com.android.systemui.recents.Recents;
+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.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
+/**
+ * A helper class to create transitions to/from Recents
+ */
+public class RecentsTransitionHelper {
+
+ private static final String TAG = "RecentsTransitionHelper";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Special value for {@link #mAppTransitionAnimationSpecs}: Indicate that we are currently
+ * waiting for the specs to be retrieved.
+ */
+ private static final List<AppTransitionAnimationSpec> SPECS_WAITING = new ArrayList<>();
+
+ @GuardedBy("this")
+ private List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs = SPECS_WAITING;
+
+ private Context mContext;
+ private Handler mHandler;
+ private TaskViewTransform mTmpTransform = new TaskViewTransform();
+
+ private Runnable mStartScreenPinningRunnable = new Runnable() {
+ @Override
+ public void run() {
+ EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext));
+ }
+ };
+
+ public RecentsTransitionHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ /**
+ * Launches the specified {@link Task}.
+ */
+ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+ final TaskStackView stackView, final TaskView taskView,
+ final boolean lockToTask, final Rect bounds, int destinationStack) {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ if (bounds != null) {
+ opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
+ }
+
+ final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ final IAppTransitionAnimationSpecsFuture transitionFuture;
+ if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
+ task.thumbnail.getHeight() > 0) {
+ transitionFuture = getAppTransitionFuture(task, stackView, destinationStack);
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+
+ if (lockToTask) {
+ // Request screen pinning after the animation runs
+ mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+ }
+ }
+ };
+ } else {
+ // This is only the case if the task is not on screen (scrolled offscreen for example)
+ transitionFuture = null;
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ }
+ };
+ }
+
+ if (taskView == null) {
+ // If there is no task view, then we do not need to worry about animating out occluding
+ // task views, and we can launch immediately
+ startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener);
+ } else {
+ if (task.group != null && !task.group.isFrontMostTask(task)) {
+ stackView.startLaunchTaskAnimation(taskView, new Runnable() {
+ @Override
+ public void run() {
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ animStartedListener);
+ }
+ }, lockToTask);
+ } else {
+ stackView.startLaunchTaskAnimation(taskView, null, lockToTask);
+ startTaskActivity(stack, task, taskView, opts, transitionFuture,
+ animStartedListener);
+ }
+ }
+ }
+
+ /**
+ * Starts the activity for the launch task.
+ *
+ * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
+ * we are toggling recents and the launch-to task is now offscreen.
+ */
+ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
+ ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
+ final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.startActivityFromRecents(mContext, task.key.id, task.activityLabel, opts)) {
+ // Keep track of the index of the task launch
+ int taskIndexFromFront = 0;
+ int taskIndex = stack.indexOfTask(task);
+ if (taskIndex > -1) {
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+ }
+ EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
+ } else {
+ // Dismiss the task if we fail to launch it
+ EventBus.getDefault().send(new DismissTaskViewEvent(task, taskView));
+
+ // Keep track of failed launches
+ EventBus.getDefault().send(new LaunchTaskFailedEvent());
+ }
+ if (transitionFuture != null) {
+ IRemoteCallback.Stub callback = null;
+ if (animStartedListener != null) {
+ callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (animStartedListener != null) {
+ animStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ };
+ }
+ try {
+ synchronized (this) {
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ }
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
+ callback, true /* scaleUp */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+ }
+
+ /**
+ * Creates a future which will later be queried for animation specs for this current transition.
+ */
+ private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final Task task,
+ final TaskStackView stackView, final int destinationStack) {
+ return new IAppTransitionAnimationSpecsFuture.Stub() {
+ @Override
+ public AppTransitionAnimationSpec[] get() throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (RecentsTransitionHelper.this) {
+ mAppTransitionAnimationSpecs = composeAnimationSpecs(task, stackView,
+ destinationStack);
+ RecentsTransitionHelper.this.notifyAll();
+ }
+ }
+ });
+ synchronized (RecentsTransitionHelper.this) {
+ while (mAppTransitionAnimationSpecs == SPECS_WAITING) {
+ try {
+ RecentsTransitionHelper.this.wait();
+ } catch (InterruptedException e) {}
+ }
+ if (mAppTransitionAnimationSpecs == null) {
+ return null;
+ }
+ AppTransitionAnimationSpec[] specs
+ = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()];
+ mAppTransitionAnimationSpecs.toArray(specs);
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ return specs;
+ }
+ }
+ };
+ }
+
+ /**
+ * Composes the animation specs for all the tasks in the target stack.
+ */
+ private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
+ final TaskStackView stackView, final int destinationStack) {
+ // Ensure we have a valid target stack id
+ final int targetStackId = destinationStack != INVALID_STACK_ID ?
+ destinationStack : task.key.stackId;
+ if (!StackId.useAnimationSpecForAppTransition(targetStackId)) {
+ return null;
+ }
+
+ // Calculate the offscreen task rect (for tasks that are not backed by views)
+ float stackScroll = stackView.getScroller().getStackScroll();
+ TaskView taskView = stackView.getChildViewForTask(task);
+ TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm();
+ Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect);
+ offscreenTaskRect.offsetTo(offscreenTaskRect.left,
+ layoutAlgorithm.mCurrentStackRect.bottom);
+
+ // If this is a full screen stack, the transition will be towards the single, full screen
+ // task. We only need the transition spec for this task.
+ List<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (taskView == null) {
+ specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
+ } else {
+ layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+ specs.add(composeAnimationSpec(taskView, mTmpTransform, true /* addHeaderBitmap */));
+ }
+ return specs;
+ }
+
+ // Otherwise, for freeform tasks, create a new animation spec for each task we have to
+ // launch
+ TaskStack stack = stackView.getStack();
+ ArrayList<Task> tasks = stack.getTasks();
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.isFreeformTask() || targetStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ TaskView tv = stackView.getChildViewForTask(t);
+ if (tv == null) {
+ // TODO: Create a different animation task rect for this case (though it should
+ // never happen)
+ specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
+ } else {
+ layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+ specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
+ }
+ }
+ }
+
+ return specs;
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link Task}
+ */
+ private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task,
+ Rect taskRect) {
+ return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link TaskView}
+ */
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskView taskView,
+ TaskViewTransform transform, boolean addHeaderBitmap) {
+ // Disable any focused state before we draw the header
+ // Upfront the processing of the thumbnail
+ if (taskView.isFocusedTask()) {
+ taskView.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
+ }
+
+ Bitmap b = null;
+ if (addHeaderBitmap) {
+ float scale = transform.scale;
+ int fromHeaderWidth = (int) (taskView.mHeaderView.getMeasuredWidth() * scale);
+ int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+ b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(scale, scale);
+ taskView.mHeaderView.draw(c);
+ c.setBitmap(null);
+ }
+ b = b.createAshmemBitmap();
+ }
+
+ Rect taskRect = new Rect();
+ transform.rect.round(taskRect);
+ return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index af30268..90456c0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,35 +16,21 @@
package com.android.systemui.recents.views;
-import android.app.ActivityOptions;
-import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.IRemoteCallback;
-import android.os.RemoteException;
+import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.View;
import android.view.WindowInsets;
-import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
@@ -53,8 +39,9 @@
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.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
+import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -65,8 +52,6 @@
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
/**
@@ -78,29 +63,15 @@
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
- private static final boolean ADD_HEADER_BITMAP = true;
-
- /**
- * Special value for {@link #mAppTransitionAnimationSpecs}: Indicate that we are currently
- * waiting for the specs to be retrieved.
- */
- private static final List<AppTransitionAnimationSpec> SPECS_WAITING = new ArrayList<>();
-
- private int mStackViewVisibility = View.VISIBLE;
-
- /** The RecentsView callbacks */
- public interface RecentsViewCallbacks {
- public void onTaskLaunchFailed();
- public void onAllTaskViewsDismissed();
- }
-
LayoutInflater mInflater;
+ Handler mHandler;
- ArrayList<TaskStack> mStacks;
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
- RecentsViewCallbacks mCb;
+ boolean mAwaitingFirstLayout = true;
+ boolean mLastTaskLaunchedWasFreeform;
+ RecentsTransitionHelper mTransitionHelper;
RecentsViewTouchHandler mTouchHandler;
DragView mDragView;
TaskStack.DockState[] mVisibleDockStates = {
@@ -114,10 +85,6 @@
Rect mSystemInsets = new Rect();
-
- @GuardedBy("this")
- List<AppTransitionAnimationSpec> mAppTransitionAnimationSpecs = SPECS_WAITING;
-
public RecentsView(Context context) {
super(context);
}
@@ -134,16 +101,13 @@
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
mInflater = LayoutInflater.from(context);
+ mHandler = new Handler();
+ mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mTouchHandler = new RecentsViewTouchHandler(this);
}
- /** Sets the callbacks */
- public void setCallbacks(RecentsViewCallbacks cb) {
- mCb = cb;
- }
-
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
@@ -165,12 +129,18 @@
mTaskStackView.setCallbacks(this);
addView(mTaskStackView);
}
- mTaskStackView.setVisibility(mStackViewVisibility);
// Trigger a new layout
requestLayout();
}
+ /**
+ * Returns whether the last task launched was in the freeform stack or not.
+ */
+ public boolean isLastTaskLaunchedFreeform() {
+ return mLastTaskLaunchedWasFreeform;
+ }
+
/** Gets the next task in the stack - or if the last - the top task */
public Task getNextTaskOrTopTask(Task taskToSearch) {
Task returnTask = null;
@@ -201,7 +171,7 @@
Task task = mTaskStackView.getFocusedTask();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
- onTaskViewClicked(mTaskStackView, taskView, stack, task, false, false, null,
+ onTaskViewClicked(mTaskStackView, taskView, stack, task, false, null,
INVALID_STACK_ID);
return true;
}
@@ -219,7 +189,7 @@
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(mTaskStackView, tv, stack, task, false, taskBounds != null,
+ onTaskViewClicked(mTaskStackView, tv, stack, task, false,
taskBounds, destinationStack);
return true;
}
@@ -359,6 +329,17 @@
mDragView.layout(left, top, left + mDragView.getMeasuredWidth(),
top + mDragView.getMeasuredHeight());
}
+
+ if (mAwaitingFirstLayout) {
+ mAwaitingFirstLayout = false;
+
+ // If launched via dragging from the nav bar, then we should translate the whole view
+ // down offscreen
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ if (launchState.launchedViaDragGesture) {
+ setTranslationY(getMeasuredHeight());
+ }
+ }
}
@Override
@@ -400,355 +381,21 @@
return super.verifyDrawable(who);
}
- /** Unfilters any filtered stacks */
- public boolean unfilterFilteredStacks() {
- if (mStacks != null) {
- // Check if there are any filtered stacks and unfilter them before we back out of Recents
- boolean stacksUnfiltered = false;
- int numStacks = mStacks.size();
- for (int i = 0; i < numStacks; i++) {
- TaskStack stack = mStacks.get(i);
- if (stack.hasFilteredTasks()) {
- stack.unfilterTasks();
- stacksUnfiltered = true;
- }
- }
- return stacksUnfiltered;
- }
- return false;
- }
-
public void disableLayersForOneFrame() {
if (mTaskStackView != null) {
mTaskStackView.disableLayersForOneFrame();
}
}
- private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final TaskStackView stackView,
- final TaskView clickedTask, final int offsetX, final int offsetY,
- final float stackScroll, final int destinationStack) {
- return new IAppTransitionAnimationSpecsFuture.Stub() {
- @Override
- public AppTransitionAnimationSpec[] get() throws RemoteException {
- post(new Runnable() {
- @Override
- public void run() {
- synchronized (RecentsView.this) {
- mAppTransitionAnimationSpecs = getAppTransitionAnimationSpecs(stackView,
- clickedTask, offsetX, offsetY, stackScroll, destinationStack);
- RecentsView.this.notifyAll();
- }
- }
- });
- synchronized (RecentsView.this) {
- while (mAppTransitionAnimationSpecs == SPECS_WAITING) {
- try {
- RecentsView.this.wait();
- } catch (InterruptedException e) {}
- }
- if (mAppTransitionAnimationSpecs == null) {
- return null;
- }
- AppTransitionAnimationSpec[] specs
- = new AppTransitionAnimationSpec[mAppTransitionAnimationSpecs.size()];
- mAppTransitionAnimationSpecs.toArray(specs);
- mAppTransitionAnimationSpecs = SPECS_WAITING;
- return specs;
- }
- }
- };
- }
-
- private List<AppTransitionAnimationSpec> getAppTransitionAnimationSpecs(TaskStackView stackView,
- TaskView clickedTask, int offsetX, int offsetY, float stackScroll,
- int destinationStack) {
- final int targetStackId = destinationStack != INVALID_STACK_ID ?
- destinationStack : clickedTask.getTask().key.stackId;
- if (targetStackId != FREEFORM_WORKSPACE_STACK_ID
- && targetStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
- return null;
- }
- // If this is a full screen stack, the transition will be towards the single, full screen
- // task. We only need the transition spec for this task.
- List<AppTransitionAnimationSpec> specs = new ArrayList<>();
- if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
- specs.add(createThumbnailHeaderAnimationSpec(
- stackView, offsetX, offsetY, stackScroll, clickedTask,
- clickedTask.getTask().key.id, ADD_HEADER_BITMAP));
- return specs;
- }
- // This is a free form stack or full screen stack, so there will be multiple windows
- // animating from thumbnails. We need transition animation specs for all of them.
-
- // We will use top and bottom task views as a base for tasks, that aren't visible on the
- // screen. This is necessary for cascade recents list, where some of the tasks might be
- // hidden.
- List<TaskView> taskViews = stackView.getTaskViews();
- int childCount = taskViews.size();
- TaskView topChild = taskViews.get(0);
- TaskView bottomChild = taskViews.get(childCount - 1);
- SparseArray<TaskView> taskViewsByTaskId = new SparseArray<>();
- for (int i = 0; i < childCount; i++) {
- TaskView taskView = taskViews.get(i);
- taskViewsByTaskId.put(taskView.getTask().key.id, taskView);
- }
-
- TaskStack stack = stackView.getStack();
- // We go through all tasks now and for each generate transition animation spec. If there is
- // a view associated with a task, we use that view as a base for the animation. If there
- // isn't, we use bottom or top view, depending on which one would be closer to the task
- // view if it existed.
- ArrayList<Task> tasks = stack.getTasks();
- boolean passedClickedTask = false;
- for (int i = 0, n = tasks.size(); i < n; i++) {
- Task task = tasks.get(i);
- TaskView taskView = taskViewsByTaskId.get(task.key.id);
- if (taskView != null) {
- specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
- stackScroll, taskView, taskView.getTask().key.id, ADD_HEADER_BITMAP));
- if (taskView == clickedTask) {
- passedClickedTask = true;
- }
- } else {
- taskView = passedClickedTask ? bottomChild : topChild;
- specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
- stackScroll, taskView, task.key.id, !ADD_HEADER_BITMAP));
- }
- }
-
- return specs;
- }
-
- private AppTransitionAnimationSpec createThumbnailHeaderAnimationSpec(TaskStackView stackView,
- int offsetX, int offsetY, float stackScroll, TaskView tv, int taskId,
- boolean addHeaderBitmap) {
- // Disable any focused state before we draw the header
- // Upfront the processing of the thumbnail
- if (tv.isFocusedTask()) {
- tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
- }
- TaskViewTransform transform = new TaskViewTransform();
- transform = stackView.getStackAlgorithm().getStackTransform(tv.mTask, stackScroll,
- transform, null);
-
- float scale = tv.getScaleX();
- int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
- int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
-
- Bitmap b = null;
- if (addHeaderBitmap) {
- b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
- Bitmap.Config.ARGB_8888);
-
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- b.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(b);
- c.scale(tv.getScaleX(), tv.getScaleY());
- tv.mHeaderView.draw(c);
- c.setBitmap(null);
-
- }
- b = b.createAshmemBitmap();
- }
-
- int[] pts = new int[2];
- tv.getLocationOnScreen(pts);
-
- final int left = pts[0] + offsetX;
- final int top = pts[1] + offsetY;
- final Rect rect = new Rect(left, top, left + (int) transform.rect.width(),
- top + (int) transform.rect.height());
-
- return new AppTransitionAnimationSpec(taskId, b, rect);
- }
-
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
final TaskStack stack, final Task task, final boolean lockToTask,
- final boolean boundsValid, final Rect bounds, int destinationStack) {
-
- // Upfront the processing of the thumbnail
- TaskViewTransform transform = new TaskViewTransform();
- View sourceView;
- int offsetX = 0;
- int offsetY = 0;
- float stackScroll = stackView.getScroller().getStackScroll();
- if (tv == null) {
- // If there is no actual task view, then use the stack view as the source view
- // and then offset to the expected transform rect, but bound this to just
- // outside the display rect (to ensure we don't animate from too far away)
- sourceView = stackView;
- offsetX = (int) transform.rect.left;
- offsetY = getMeasuredHeight();
- } else {
- sourceView = tv.mThumbnailView;
- }
-
- // Compute the thumbnail to scale up from
- final SystemServicesProxy ssp = Recents.getSystemServices();
- boolean screenPinningRequested = false;
- ActivityOptions opts = ActivityOptions.makeBasic();
- ActivityOptions.OnAnimationStartedListener animStartedListener = null;
- final IAppTransitionAnimationSpecsFuture transitionFuture;
- if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
- task.thumbnail.getHeight() > 0) {
- animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted() {
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-
- if (lockToTask) {
- // Request screen pinning after the animation runs
- postDelayed(new Runnable() {
- @Override
- public void run() {
- EventBus.getDefault().send(new ScreenPinningRequestEvent(
- getContext(), ssp));
- }
- }, 350);
- }
- }
- };
- transitionFuture = getAppTransitionFuture(stackView, tv, offsetX, offsetY, stackScroll,
- destinationStack);
- screenPinningRequested = true;
- } else {
- transitionFuture = null;
- }
- if (boundsValid) {
- opts.setBounds(bounds.isEmpty() ? null : bounds);
- }
- final ActivityOptions launchOpts = opts;
- final boolean finalScreenPinningRequested = screenPinningRequested;
- final OnAnimationStartedListener finalAnimStartedListener = animStartedListener;
- final Runnable launchRunnable = new Runnable() {
- @Override
- public void run() {
- if (task.isActive) {
- // Bring an active task to the foreground
- ssp.moveTaskToFront(task.key.id, launchOpts);
- } else {
- if (ssp.startActivityFromRecents(getContext(), task.key.id, task.activityLabel,
- launchOpts)) {
- if (!finalScreenPinningRequested) {
- // If we have not requested this already to be run after the window
- // transition, then just run it now
- EventBus.getDefault().send(new ScreenPinningRequestEvent(
- getContext(), ssp));
- }
- } else {
- // Dismiss the task and return the user to home if we fail to
- // launch the task
- EventBus.getDefault().send(new DismissTaskViewEvent(task, tv));
- if (mCb != null) {
- mCb.onTaskLaunchFailed();
- }
-
- // Keep track of failed launches
- MetricsLogger.count(getContext(), "overview_task_launch_failed", 1);
- }
- }
- if (transitionFuture != null) {
- IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- post(new Runnable() {
- @Override
- public void run() {
- if (finalAnimStartedListener != null) {
- finalAnimStartedListener.onAnimationStarted();
- }
- }
- });
- }
- };
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
- callback, true /* scaleUp */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override transition: " + e);
- }
- }
- }
- };
-
- // Keep track of the index of the task launch
- int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfTask(task);
- if (taskIndex > -1) {
- taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
- }
- MetricsLogger.histogram(getContext(), "overview_task_launch_index", taskIndexFromFront);
-
- // Launch the app right away if there is no task view, otherwise, animate the icon out first
- if (tv == null) {
- launchRunnable.run();
- } else {
- if (task.group != null && !task.group.isFrontMostTask(task)) {
- // For affiliated tasks that are behind other tasks, we must animate the front cards
- // out of view before starting the task transition
- stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
- } else {
- // Otherwise, we can start the task transition immediately
- stackView.startLaunchTaskAnimation(tv, null, lockToTask);
- launchRunnable.run();
- }
- }
- }
-
- @Override
- public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks) {
- /* TODO: Not currently enabled
- if (removedTasks != null) {
- int taskCount = removedTasks.size();
- for (int i = 0; i < taskCount; i++) {
- onTaskViewDismissed(removedTasks.get(i));
- }
- }
- */
-
- mCb.onAllTaskViewsDismissed();
-
- // Keep track of all-deletions
- MetricsLogger.count(getContext(), "overview_task_all_dismissed", 1);
- }
-
- @Override
- public void onTaskStackFilterTriggered() {
- // Hide the search bar
- if (mSearchBar != null) {
- int filterDuration = getResources().getInteger(
- R.integer.recents_filter_animate_current_views_duration);
- mSearchBar.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setDuration(filterDuration)
- .withLayer()
- .start();
- }
- }
-
- @Override
- public void onTaskStackUnfilterTriggered() {
- // Show the search bar
- if (mSearchBar != null) {
- int filterDuration = getResources().getInteger(
- R.integer.recents_filter_animate_new_views_duration);
- mSearchBar.animate()
- .alpha(1f)
- .setStartDelay(0)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setDuration(filterDuration)
- .withLayer()
- .start();
- }
+ final Rect bounds, int destinationStack) {
+ mLastTaskLaunchedWasFreeform = SystemServicesProxy.isFreeformStack(task.key.stackId);
+ mTransitionHelper.launchTaskFromRecents(stack, task, stackView, tv, lockToTask, bounds,
+ destinationStack);
}
/**** EventBus Events ****/
@@ -835,6 +482,16 @@
}
}
+ public final void onBusEvent(DraggingInRecentsEvent event) {
+ if (mTaskStackView.getTaskViews().size() > 0) {
+ setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
+ }
+ }
+
+ public final void onBusEvent(DraggingInRecentsEndedEvent event) {
+ animate().translationY(0f);
+ }
+
/**
* Updates the dock region to match the specified dock state.
*/
@@ -861,11 +518,11 @@
}
}
- public void setStackViewVisibility(int stackViewVisibility) {
- mStackViewVisibility = stackViewVisibility;
- if (mTaskStackView != null) {
- mTaskStackView.setVisibility(stackViewVisibility);
- invalidate();
+ public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+ if (!event.visible) {
+ // Reset the view state
+ mAwaitingFirstLayout = true;
+ mLastTaskLaunchedWasFreeform = false;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index c4e2d8a..5a09ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -26,7 +26,7 @@
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
@@ -81,21 +81,11 @@
/**
* Starts animating the scrim views when entering Recents.
*/
- public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- int transitionEnterFromAppDelay = mContext.getResources().getInteger(
- R.integer.recents_enter_from_app_transition_duration);
- int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
- R.integer.recents_enter_from_home_transition_duration);
-
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
mStatusBarScrimView.animate()
.translationY(0)
- .setStartDelay(launchState.launchedFromHome ?
- transitionEnterFromHomeDelay :
- transitionEnterFromAppDelay)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@@ -110,9 +100,6 @@
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
.translationY(0)
- .setStartDelay(launchState.launchedFromHome ?
- transitionEnterFromHomeDelay :
- transitionEnterFromAppDelay)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 51091c3..5d091b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -16,14 +16,22 @@
package com.android.systemui.recents.views;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Path;
import android.graphics.Rect;
+import android.util.FloatProperty;
import android.util.Log;
+import android.util.Property;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.ParametricCurve;
+import com.android.systemui.recents.RecentsDebugFlags;
+import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -32,21 +40,96 @@
import java.util.ArrayList;
import java.util.HashMap;
+/**
+ * Used to describe a visible range that can be normalized to [0, 1].
+ */
+class Range {
+ final float relativeMin;
+ final float relativeMax;
+ float origin;
+ float min;
+ float max;
+
+ public Range(float relMin, float relMax) {
+ min = relativeMin = relMin;
+ max = relativeMax = relMax;
+ }
+
+ /**
+ * Offsets this range to a given absolute position.
+ */
+ public void offset(float x) {
+ this.origin = x;
+ min = x + relativeMin;
+ max = x + relativeMax;
+ }
+
+ /**
+ * Returns x normalized to the range 0 to 1 such that 0 = min, 0.5 = origin and 1 = max
+ *
+ * @param x is an absolute value in the same domain as origin
+ */
+ public float getNormalizedX(float x) {
+ if (x < origin) {
+ return 0.5f + 0.5f * (x - origin) / -relativeMin;
+ } else {
+ return 0.5f + 0.5f * (x - origin) / relativeMax;
+ }
+ }
+
+ /**
+ * Given a normalized {@param x} value in this range, projected onto the full range to get an
+ * absolute value about the given {@param origin}.
+ */
+ public float getAbsoluteX(float normX) {
+ if (normX < 0.5f) {
+ return (normX - 0.5f) / 0.5f * -relativeMin;
+ } else {
+ return (normX - 0.5f) / 0.5f * relativeMax;
+ }
+ }
+
+ /**
+ * Returns whether a value at an absolute x would be within range.
+ */
+ public boolean isInRange(float absX) {
+ return (absX >= Math.floor(min)) && (absX <= Math.ceil(max));
+ }
+}
/**
- * The layout logic for a TaskStackView.
+ * The layout logic for a TaskStackView. This layout can have two states focused and unfocused,
+ * and in the focused state, there is a task that is displayed more prominently in the stack.
*/
public class TaskStackLayoutAlgorithm {
private static final String TAG = "TaskStackViewLayoutAlgorithm";
private static final boolean DEBUG = false;
- // The min scale of the last task at the top of the curve
- private static final float STACK_PEEK_MIN_SCALE = 0.85f;
- // The scale of the last task
- private static final float SINGLE_TASK_SCALE = 0.95f;
- // The percentage of height of task to show between tasks
- private static final float VISIBLE_TASK_HEIGHT_BETWEEN_TASKS = 0.5f;
+ // The scale factor to apply to the user movement in the stack to unfocus it
+ private static final float UNFOCUS_MULTIPLIER = 0.8f;
+
+ // The various focus states
+ public static final float STATE_FOCUSED = 1f;
+ public static final float STATE_UNFOCUSED = 0f;
+
+ /**
+ * A Property wrapper around the <code>focusState</code> functionality handled by the
+ * {@link TaskStackLayoutAlgorithm#setFocusState(float)} and
+ * {@link TaskStackLayoutAlgorithm#getFocusState()} methods.
+ */
+ private static final Property<TaskStackLayoutAlgorithm, Float> FOCUS_STATE =
+ new FloatProperty<TaskStackLayoutAlgorithm>("focusState") {
+ @Override
+ public void setValue(TaskStackLayoutAlgorithm object, float value) {
+ object.setFocusState(value);
+ }
+
+ @Override
+ public Float get(TaskStackLayoutAlgorithm object) {
+ return object.getFocusState();
+ }
+ };
// A report of the visibility state of the stack
public class VisibilityReport {
@@ -61,6 +144,8 @@
}
Context mContext;
+ private TaskStackView mStackView;
+ private Interpolator mFastOutSlowInInterpolator;
// The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
public Rect mTaskRect = new Rect();
@@ -74,10 +159,33 @@
private Rect mStackRect = new Rect();
// The current stack rect, can either by mFreeformStackRect or mStackRect depending on whether
// there is a freeform workspace
- public Rect mCurrentStackRect;
+ public Rect mCurrentStackRect = new Rect();
// This is the current system insets
public Rect mSystemInsets = new Rect();
+ // The visible ranges when the stack is focused and unfocused
+ private Range mUnfocusedRange;
+ private Range mFocusedRange;
+
+ // The offset from the top when scrolled to the top of the stack
+ private int mFocusedPeekHeight;
+
+ // The offset from the bottom of the stack to the bottom of the bounds
+ private int mStackBottomOffset;
+
+ // The paths defining the motion of the tasks when the stack is focused and unfocused
+ private Path mUnfocusedCurve;
+ private Path mFocusedCurve;
+ private FreePathInterpolator mUnfocusedCurveInterpolator;
+ private FreePathInterpolator mFocusedCurveInterpolator;
+
+ // The state of the stack focus (0..1), which controls the transition of the stack from the
+ // focused to non-focused state
+ private float mFocusState;
+
+ // The animator used to reset the focused state
+ private ObjectAnimator mFocusStateAnimator;
+
// The smallest scroll progress, at this value, the back most task will be visible
float mMinScrollP;
// The largest scroll progress, at this value, the front most task will be visible above the
@@ -88,78 +196,44 @@
// The task progress for the front-most task in the stack
float mFrontMostTaskP;
- // The relative progress to ensure that the height between affiliated tasks is respected
- float mWithinAffiliationPOffset;
- // The relative progress to ensure that the height between non-affiliated tasks is
- // respected
- float mBetweenAffiliationPOffset;
- // The relative progress to ensure that the task height is respected
- float mTaskHeightPOffset;
- // The relative progress to ensure that the half task height is respected
- float mTaskHalfHeightPOffset;
- // The front-most task bottom offset
- int mStackBottomOffset;
- // The relative progress to ensure that the offset from the bottom of the stack to the bottom
- // of the task is respected
- float mStackBottomPOffset;
-
// The last computed task counts
int mNumStackTasks;
int mNumFreeformTasks;
+
// The min/max z translations
int mMinTranslationZ;
int mMaxTranslationZ;
- // Optimization, allows for quick lookup of task -> progress
- HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<>();
+ // Optimization, allows for quick lookup of task -> index
+ private HashMap<Task.TaskKey, Integer> mTaskIndexMap = new HashMap<>();
// The freeform workspace layout
FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
- // Log function
- static ParametricCurve sCurve;
-
- public TaskStackLayoutAlgorithm(Context context) {
+ public TaskStackLayoutAlgorithm(Context context, TaskStackView stackView) {
Resources res = context.getResources();
+ mStackView = stackView;
+
+ mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
+ res.getFloat(R.integer.recents_layout_focused_range_max));
+ mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
+ res.getFloat(R.integer.recents_layout_unfocused_range_max));
+ mFocusState = getDefaultFocusState();
+ mFocusedPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_focused_peek_size);
+
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
mContext = context;
mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm();
- if (sCurve == null) {
- sCurve = new ParametricCurve(new ParametricCurve.CurveFunction() {
- // The large the XScale, the longer the flat area of the curve
- private static final float XScale = 1.75f;
- private static final float LogBase = 3000;
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ }
- float reverse(float x) {
- return (-x * XScale) + 1;
- }
-
- @Override
- public float f(float x) {
- return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
- }
-
- @Override
- public float invF(float y) {
- return (float) (Math.log(1f - reverse(y)) / (-Math.log(LogBase) * XScale));
- }
- }, new ParametricCurve.ParametricCurveFunction() {
- @Override
- public float f(float p) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.hasFreeformWorkspaceSupport()) {
- return 1f;
- }
-
- if (p < 0) return STACK_PEEK_MIN_SCALE;
- if (p > 1) return 1f;
- float scaleRange = (1f - STACK_PEEK_MIN_SCALE);
- float scale = STACK_PEEK_MIN_SCALE + (p * scaleRange);
- return scale;
- }
- });
- }
+ /**
+ * Resets this layout when the stack view is reset.
+ */
+ public void reset() {
+ setFocusState(getDefaultFocusState());
}
/**
@@ -173,16 +247,32 @@
}
/**
+ * Sets the focused state.
+ */
+ public void setFocusState(float focusState) {
+ mFocusState = focusState;
+ mStackView.requestSynchronizeStackViewsWithModel();
+ }
+
+ /**
+ * Gets the focused state.
+ */
+ public float getFocusState() {
+ return mFocusState;
+ }
+
+ /**
* Computes the stack and task rects. The given task stack bounds is the whole bounds not
* including the search bar.
*/
public void initialize(Rect taskStackBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
-
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
int heightPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_stack_top_padding);
+ Rect lastStackRect = new Rect(mCurrentStackRect);
// The freeform height is the visible height (not including system insets) - padding above
// freeform and below stack - gap between the freeform and stack
@@ -200,43 +290,35 @@
taskStackBounds.top + heightPadding,
taskStackBounds.right - widthPadding,
taskStackBounds.bottom);
+
// Anchor the task rect to the top-center of the non-freeform stack rect
- int size = Math.min(mStackRect.width(), mStackRect.height() - mStackBottomOffset);
+ float aspect = (float) (taskStackBounds.width() - mSystemInsets.left - mSystemInsets.right)
+ / (taskStackBounds.height() - mSystemInsets.bottom);
+ int width = mStackRect.width();
+ int height = debugFlags.isFullscreenThumbnailsEnabled() ? (int) (width / aspect) : width;
mTaskRect.set(mStackRect.left, mStackRect.top,
- mStackRect.left + size, mStackRect.top + size);
+ mStackRect.left + width, mStackRect.top + height);
mCurrentStackRect = ssp.hasFreeformWorkspaceSupport() ? mFreeformStackRect : mStackRect;
- // Compute the progress offsets
- int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_bar_height);
- int betweenAffiliationOffset = (int) (VISIBLE_TASK_HEIGHT_BETWEEN_TASKS * mTaskRect.height());
- mWithinAffiliationPOffset = sCurve.computePOffsetForScaledHeight(withinAffiliationOffset,
- mCurrentStackRect);
- mBetweenAffiliationPOffset = sCurve.computePOffsetForScaledHeight(betweenAffiliationOffset,
- mCurrentStackRect);
- mTaskHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height(),
- mCurrentStackRect);
- mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
- mCurrentStackRect);
- mStackBottomPOffset = sCurve.computePOffsetForHeight(mStackBottomOffset, mCurrentStackRect);
+ // Short circuit here if the stack rects haven't changed so we don't do all the work below
+ if (lastStackRect.equals(mCurrentStackRect)) {
+ return;
+ }
+
+ // Reinitialize the focused and unfocused curves
+ mUnfocusedCurve = constructUnfocusedCurve();
+ mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
+ mFocusedCurve = constructFocusedCurve();
+ mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
if (DEBUG) {
Log.d(TAG, "initialize");
- Log.d(TAG, "\tarclength: " + sCurve.getArcLength());
Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
Log.d(TAG, "\tmFreeformStackRect: " + mFreeformStackRect);
Log.d(TAG, "\tmStackRect: " + mStackRect);
+ Log.d(TAG, "\tmCurrentStackRect: " + mCurrentStackRect);
Log.d(TAG, "\tmTaskRect: " + mTaskRect);
Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
-
- Log.d(TAG, "\tpWithinAffiliateOffset: " + mWithinAffiliationPOffset);
- Log.d(TAG, "\tpBetweenAffiliateOffset: " + mBetweenAffiliationPOffset);
- Log.d(TAG, "\tmTaskHeightPOffset: " + mTaskHeightPOffset);
- Log.d(TAG, "\tmTaskHalfHeightPOffset: " + mTaskHalfHeightPOffset);
- Log.d(TAG, "\tmStackBottomPOffset: " + mStackBottomPOffset);
-
- Log.d(TAG, "\ty at p=0: " + sCurve.pToX(0f, mCurrentStackRect));
- Log.d(TAG, "\ty at p=1: " + sCurve.pToX(1f, mCurrentStackRect));
}
}
@@ -248,7 +330,7 @@
SystemServicesProxy ssp = Recents.getSystemServices();
// Clear the progress map
- mTaskProgressMap.clear();
+ mTaskIndexMap.clear();
// Return early if we have no tasks
ArrayList<Task> tasks = stack.getTasks();
@@ -273,41 +355,28 @@
mNumStackTasks = stackTasks.size();
mNumFreeformTasks = freeformTasks.size();
- float pAtBackMostTaskTop = 0;
- float pAtFrontMostTaskTop = pAtBackMostTaskTop;
- if (!stackTasks.isEmpty()) {
- // Update the for each task from back to front.
- int taskCount = stackTasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = stackTasks.get(i);
- mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
+ // Put each of the tasks in the progress map at a fixed index (does not need to actually
+ // map to a scroll position, just by index)
+ int taskCount = stackTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = stackTasks.get(i);
+ mTaskIndexMap.put(task.key, i);
+ }
- if (DEBUG) {
- Log.d(TAG, "Update: " + task.activityLabel + " p: " + pAtFrontMostTaskTop);
- }
-
- if (i < (taskCount - 1)) {
- // Increment the peek height
- float pPeek = task.group == null || task.group.isFrontMostTask(task) ?
- mBetweenAffiliationPOffset : mWithinAffiliationPOffset;
- pAtFrontMostTaskTop += pPeek;
- }
- }
-
- mFrontMostTaskP = pAtFrontMostTaskTop;
- if (mNumStackTasks > 1) {
- // Set the stack end scroll progress to the point at which the bottom of the front-most
- // task is aligned to the bottom of the stack
- mMaxScrollP = alignToStackBottom(pAtFrontMostTaskTop,
- mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ?
- mTaskHalfHeightPOffset : mTaskHeightPOffset));
- // Basically align the back-most task such that the last two tasks would be visible
- mMinScrollP = alignToStackBottom(pAtBackMostTaskTop,
- mStackBottomPOffset + (ssp.hasFreeformWorkspaceSupport() ?
- mTaskHalfHeightPOffset : mTaskHeightPOffset));
- } else {
- // When there is a single item, then just make all the stack progresses the same
+ // Calculate the min/max scroll
+ if (getDefaultFocusState() > 0f) {
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+ } else {
+ if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
mMinScrollP = mMaxScrollP = 0;
+ } else {
+ float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) /
+ mCurrentStackRect.height();
+ float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP,
+ (mNumStackTasks - 1) - Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
}
}
@@ -315,7 +384,20 @@
mFreeformLayoutAlgorithm.update(freeformTasks, this);
mInitialScrollP = mMaxScrollP;
} else {
- mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
+ if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
+ mInitialScrollP = mMinScrollP;
+ } else if (getDefaultFocusState() > 0f) {
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ if (launchState.launchedFromHome) {
+ mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+ } else {
+ mInitialScrollP = Math.max(mMinScrollP, mNumStackTasks - 2);
+ }
+ } else {
+ float offsetPct = (float) (mTaskRect.height() / 2) / mCurrentStackRect.height();
+ float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
+ mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX);
+ }
}
if (DEBUG) {
@@ -327,8 +409,45 @@
}
/**
- * Computes the maximum number of visible tasks and thumbnails. Requires that
- * update() is called first.
+ * Updates this stack when a scroll happens.
+ */
+ public void updateFocusStateOnScroll(int yMovement) {
+ Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator);
+ if (mFocusState > STATE_UNFOCUSED) {
+ float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mCurrentStackRect.height());
+ mFocusState -= Math.min(mFocusState, Math.abs(delta));
+ }
+ }
+
+ /**
+ * Aniamtes the focused state back to its orginal state.
+ */
+ public void animateFocusState(float newState) {
+ Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator);
+ if (Float.compare(newState, getFocusState()) != 0) {
+ mFocusStateAnimator = ObjectAnimator.ofFloat(this, FOCUS_STATE, getFocusState(),
+ newState);
+ mFocusStateAnimator.setDuration(200);
+ mFocusStateAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mFocusStateAnimator.start();
+ }
+ }
+
+ /**
+ * Returns the default focus state.
+ */
+ public float getDefaultFocusState() {
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (debugFlags.isPageOnToggleEnabled() || launchState.launchedWithAltTab) {
+ return 1f;
+ }
+ return 0f;
+ }
+
+ /**
+ * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
+ * stack scroll. Requires that update() is called first.
*/
public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
// Ensure minimum visibility count
@@ -344,29 +463,32 @@
// Otherwise, walk backwards in the stack and count the number of tasks and visible
// thumbnails and add that to the total freeform task count
- int taskHeight = mTaskRect.height();
+ TaskViewTransform tmpTransform = new TaskViewTransform();
+ Range currentRange = getDefaultFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
+ currentRange.offset(mInitialScrollP);
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
int numVisibleTasks = Math.max(mNumFreeformTasks, 1);
int numVisibleThumbnails = Math.max(mNumFreeformTasks, 1);
- Task firstNonFreeformTask = tasks.get(tasks.size() - mNumFreeformTasks - 1);
- float progress = mTaskProgressMap.get(firstNonFreeformTask.key) - mInitialScrollP;
- int prevScreenY = sCurve.pToX(progress, mCurrentStackRect);
- for (int i = tasks.size() - 2; i >= 0; i--) {
+ float prevScreenY = Integer.MAX_VALUE;
+ for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
+
+ // Skip freeform
if (task.isFreeformTask()) {
continue;
}
- progress = mTaskProgressMap.get(task.key) - mInitialScrollP;
- if (progress < 0) {
- break;
+ // Skip invisible
+ float taskProgress = getStackScrollForTask(task);
+ if (!currentRange.isInRange(taskProgress)) {
+ continue;
}
+
boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
if (isFrontMostTaskInGroup) {
- float scaleAtP = sCurve.pToScale(progress);
- int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
- int screenY = sCurve.pToX(progress, mCurrentStackRect) + scaleYOffsetAtP;
+ getStackTransform(taskProgress, mInitialScrollP, tmpTransform, null);
+ float screenY = tmpTransform.rect.top;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
numVisibleThumbnails++;
@@ -374,12 +496,12 @@
prevScreenY = screenY;
} else {
// Once we hit the next front most task that does not have a visible thumbnail,
- // w alk through remaining visible set
+ // walk through remaining visible set
for (int j = i; j >= 0; j--) {
numVisibleTasks++;
- progress = mTaskProgressMap.get(tasks.get(j).key) - mInitialScrollP;
- if (progress < 0) {
- break;
+ taskProgress = getStackScrollForTask(tasks.get(j));
+ if (!currentRange.isInRange(taskProgress)) {
+ continue;
}
}
break;
@@ -397,86 +519,82 @@
* is what the view is measured and laid out with.
*/
public TaskViewTransform getStackTransform(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ TaskViewTransform transformOut, TaskViewTransform frontTransform) {
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
return transformOut;
} else {
// Return early if we have an invalid index
- if (task == null || !mTaskProgressMap.containsKey(task.key)) {
+ if (task == null || !mTaskIndexMap.containsKey(task.key)) {
transformOut.reset();
return transformOut;
}
- return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut,
- prevTransform);
+ return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
+ frontTransform);
}
}
/** Update/get the transform */
public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform prevTransform) {
+ TaskViewTransform transformOut, TaskViewTransform frontTransform) {
SystemServicesProxy ssp = Recents.getSystemServices();
- if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
- // Center the task in the stack, changing the scale will not follow the curve, but just
- // modulate some values directly
- float pTaskRelative = mMinScrollP - stackScroll;
- float scale = ssp.hasFreeformWorkspaceSupport() ? 1f : SINGLE_TASK_SCALE;
- int topOffset = (mCurrentStackRect.top - mTaskRect.top) +
- (mCurrentStackRect.height() - mTaskRect.height()) / 2;
- transformOut.scale = scale;
- transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
- transformOut.translationY = (int) (topOffset + (pTaskRelative * mCurrentStackRect.height()));
- transformOut.translationZ = mMaxTranslationZ;
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- transformOut.p = pTaskRelative;
- return transformOut;
+ // Compute the focused and unfocused offset
+ mUnfocusedRange.offset(stackScroll);
+ float p = mUnfocusedRange.getNormalizedX(taskProgress);
+ float yp = mUnfocusedCurveInterpolator.getInterpolation(p);
+ float unfocusedP = p;
+ int unFocusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height());
+ boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
+ int focusedY = 0;
+ boolean focusedVisible = true;
+ if (mFocusState > 0f) {
+ mFocusedRange.offset(stackScroll);
+ p = mFocusedRange.getNormalizedX(taskProgress);
+ yp = mFocusedCurveInterpolator.getInterpolation(p);
+ focusedY = (int) (Math.max(0f, (1f - yp)) * mCurrentStackRect.height());
+ focusedVisible = mFocusedRange.isInRange(taskProgress);
+ }
- } else {
- float pTaskRelative = taskProgress - stackScroll;
- float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
- if (DEBUG) {
- Log.d(TAG, "getStackTransform (normal): " + taskProgress + ", " + stackScroll);
- }
-
- // If the task top is outside of the bounds below the screen, then immediately reset it
- if (pTaskRelative > 1f) {
- transformOut.reset();
- transformOut.rect.set(mTaskRect);
- return transformOut;
- }
- // The check for the top is trickier, since we want to show the next task if it is at
- // all visible, even if p < 0.
- if (pTaskRelative < 0f) {
- if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
- transformOut.reset();
- transformOut.rect.set(mTaskRect);
- return transformOut;
- }
- }
- float scale = sCurve.pToScale(pBounded);
- int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
- transformOut.scale = scale;
- transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
- transformOut.translationY = (mCurrentStackRect.top - mTaskRect.top) +
- (sCurve.pToX(pBounded, mCurrentStackRect) - mCurrentStackRect.top) -
- scaleYOffset;
- transformOut.translationZ = Math.max(mMinTranslationZ,
- mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = true;
- transformOut.p = pTaskRelative;
- if (DEBUG) {
- Log.d(TAG, "\t" + transformOut);
- }
-
+ // Skip if the task is not visible
+ if (!unfocusedVisible && !focusedVisible) {
+ transformOut.reset();
return transformOut;
}
+
+ int y;
+ float z;
+ float relP;
+ if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
+ // When there is exactly one task, then decouple the task from the stack and just move
+ // in screen space
+ p = (mMinScrollP - stackScroll) / mNumStackTasks;
+ int centerYOffset = (mCurrentStackRect.top - mTaskRect.top) +
+ (mCurrentStackRect.height() - mTaskRect.height()) / 2;
+ y = centerYOffset + getYForDeltaP(p, 0);
+ z = mMaxTranslationZ;
+ relP = p;
+
+ } else {
+ // Otherwise, update the task to the stack layout
+ y = unFocusedY + (int) (mFocusState * (focusedY - unFocusedY));
+ y += (mCurrentStackRect.top - mTaskRect.top);
+ z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
+ mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
+ relP = unfocusedP;
+ }
+
+ // Fill out the transform
+ transformOut.scale = 1f;
+ transformOut.translationX = (mCurrentStackRect.width() - mTaskRect.width()) / 2;
+ transformOut.translationY = y;
+ transformOut.translationZ = z;
+ transformOut.rect.set(mTaskRect);
+ transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+ Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+ transformOut.visible = true;
+ transformOut.p = relP;
+ return transformOut;
}
/**
@@ -491,8 +609,8 @@
* stack.
*/
float getStackScrollForTask(Task t) {
- if (!mTaskProgressMap.containsKey(t.key)) return 0f;
- return mTaskProgressMap.get(t.key);
+ if (!mTaskIndexMap.containsKey(t.key)) return 0f;
+ return mTaskIndexMap.get(t.key);
}
/**
@@ -501,7 +619,8 @@
* screen along the arc-length proportionally (1/arclength).
*/
public float getDeltaPForY(int downY, int y) {
- float deltaP = (float) (y - downY) / mCurrentStackRect.height() * (1f / sCurve.getArcLength());
+ float deltaP = (float) (y - downY) / mCurrentStackRect.height() *
+ mUnfocusedCurveInterpolator.getArcLength();
return -deltaP;
}
@@ -510,18 +629,61 @@
* of the curve, map back to the screen y.
*/
public int getYForDeltaP(float downScrollP, float p) {
- int y = (int) ((p - downScrollP) * mCurrentStackRect.height() * sCurve.getArcLength());
+ int y = (int) ((p - downScrollP) * mCurrentStackRect.height() *
+ (1f / mUnfocusedCurveInterpolator.getArcLength()));
return -y;
}
- private float alignToStackTop(float p) {
- // At scroll progress == p, then p is at the top of the stack
+ /**
+ * Creates a new path for the focused curve.
+ */
+ private Path constructFocusedCurve() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_bar_height);
+
+ // Initialize the focused curve. This curve is a piecewise curve composed of several
+ // quadradic beziers that goes from (0,1) through (0.5, peek height offset),
+ // (0.667, next task offset), (0.833, bottom task offset), and (1,0).
+ float peekHeightPct = 0f;
+ if (!ssp.hasFreeformWorkspaceSupport()) {
+ peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height();
+ }
+ Path p = new Path();
+ p.moveTo(0f, 1f);
+ p.lineTo(0.5f, 1f - peekHeightPct);
+ p.lineTo(0.66666667f, (float) (taskBarHeight * 3) / mCurrentStackRect.height());
+ p.lineTo(0.83333333f, (float) (taskBarHeight / 2) / mCurrentStackRect.height());
+ p.lineTo(1f, 0f);
return p;
}
- private float alignToStackBottom(float p, float pOffsetFromBottom) {
- // At scroll progress == p, then p is at the top of the stack
- // At scroll progress == p + 1, then p is at the bottom of the stack
- return p - (1 - pOffsetFromBottom);
+ /**
+ * Creates a new path for the unfocused curve.
+ */
+ private Path constructUnfocusedCurve() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
+ // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic
+ // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0). This
+ // ensures that we match the range, at which 0.5 represents the stack scroll at the current
+ // task progress. Because the height offset can change depending on a resource, we compute
+ // the control point of the second bezier such that between it and a first known point,
+ // there is a tangent at (0.5, peek height offset).
+ float cpoint1X = 0.4f;
+ float cpoint1Y = 1f;
+ float peekHeightPct = 0f;
+ if (!ssp.hasFreeformWorkspaceSupport()) {
+ peekHeightPct = (float) mFocusedPeekHeight / mCurrentStackRect.height();
+ }
+ float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
+ float b = 1f - slope * cpoint1X;
+ float cpoint2X = 0.75f;
+ float cpoint2Y = slope * cpoint2X + b;
+ Path p = new Path();
+ p.moveTo(0f, 1f);
+ p.cubicTo(0f, 1f, cpoint1X, 1f, 0.5f, 1f - peekHeightPct);
+ p.cubicTo(0.5f, 1f - peekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
+ return p;
}
}
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 4a11b93..67710bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -38,10 +38,17 @@
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.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
@@ -60,7 +67,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -79,15 +85,11 @@
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
- boolean lockToTask, boolean boundsValid, Rect bounds, int destinationStack);
- public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
- public void onTaskStackFilterTriggered();
- public void onTaskStackUnfilterTriggered();
+ boolean lockToTask, Rect bounds, int destinationStack);
}
TaskStack mStack;
TaskStackLayoutAlgorithm mLayoutAlgorithm;
- TaskStackViewFilterAlgorithm mFilterAlgorithm;
TaskStackViewScroller mStackScroller;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
@@ -101,6 +103,7 @@
boolean mStackViewsDirty = true;
boolean mStackViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
+ boolean mEnterAnimationComplete = false;
boolean mStartEnterAnimationRequestedAfterLayout;
ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
@@ -114,6 +117,7 @@
HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
ArrayList<TaskView> mTaskViews = new ArrayList<>();
List<TaskView> mImmutableTaskViews = new ArrayList<>();
+ List<TaskView> mTmpTaskViews = new ArrayList<>();
LayoutInflater mInflater;
boolean mLayersDisabled;
boolean mTouchExplorationEnabled;
@@ -150,8 +154,7 @@
setStack(stack);
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context);
- mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
+ mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
@@ -279,14 +282,9 @@
}
// Mark each task view for relayout
- if (mViewPool != null) {
- Iterator<TaskView> iter = mViewPool.poolViewIterator();
- if (iter != null) {
- while (iter.hasNext()) {
- TaskView tv = iter.next();
- tv.reset();
- }
- }
+ List<TaskView> poolViews = mViewPool.getViews();
+ for (TaskView tv : poolViews) {
+ tv.reset();
}
// Reset the stack state
@@ -294,11 +292,14 @@
mStackViewsDirty = true;
mStackViewsClipDirty = true;
mAwaitingFirstLayout = true;
+ mEnterAnimationComplete = false;
if (mUIDozeTrigger != null) {
mUIDozeTrigger.stopDozing();
mUIDozeTrigger.resetTrigger();
}
mStackScroller.reset();
+ mLayoutAlgorithm.reset();
+ requestLayout();
}
/** Requests that the views be synchronized with the model */
@@ -357,7 +358,7 @@
}
// Update the stack transforms
- TaskViewTransform prevTransform = null;
+ TaskViewTransform frontTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
Task task = tasks.get(i);
if (task.isFreeformTask()) {
@@ -365,7 +366,7 @@
}
TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
- taskTransforms.get(i), prevTransform);
+ taskTransforms.get(i), frontTransform);
if (DEBUG) {
Log.d(TAG, "updateStackTransform: " + i + ", " + transform.visible);
}
@@ -390,7 +391,7 @@
transform.translationY = Math.min(transform.translationY,
mLayoutAlgorithm.mCurrentStackRect.bottom);
}
- prevTransform = transform;
+ frontTransform = transform;
}
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
@@ -609,15 +610,19 @@
/**
* Sets the focused task to the provided (bounded taskIndex).
+ *
+ * @return whether or not the stack will scroll as a part of this focus change
*/
- private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
- setFocusedTask(taskIndex, scrollToTask, animated, true);
+ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
+ return setFocusedTask(taskIndex, scrollToTask, animated, true);
}
/**
* Sets the focused task to the provided (bounded taskIndex).
+ *
+ * @return whether or not the stack will scroll as a part of this focus change
*/
- private void setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
+ private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
final boolean requestViewFocus) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
@@ -633,6 +638,7 @@
}
}
+ boolean willScroll = false;
mFocusedTaskIndex = newFocusedTaskIndex;
if (mFocusedTaskIndex != -1) {
Runnable focusTaskRunnable = new Runnable() {
@@ -647,19 +653,39 @@
if (scrollToTask) {
// TODO: Center the newly focused task view, only if not freeform
- float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask) - 0.5f;
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask);
+ if (!debugFlags.isFullscreenThumbnailsEnabled()) {
+ newScroll -= 0.5f;
+ }
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
- mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
- focusTaskRunnable);
+ if (Float.compare(newScroll, mStackScroller.getStackScroll()) != 0) {
+ mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
+ focusTaskRunnable);
+ willScroll = true;
+
+ // Cancel any running enter animations at this point when we scroll as well
+ if (!mEnterAnimationComplete) {
+ final List<TaskView> taskViews = getTaskViews();
+ for (TaskView tv : taskViews) {
+ tv.cancelEnterRecentsAnimation();
+ }
+ }
+ } else {
+ focusTaskRunnable.run();
+ }
+ mLayoutAlgorithm.animateFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
} else {
focusTaskRunnable.run();
}
}
+ return willScroll;
}
/**
* Sets the focused task relative to the currently focused task.
*
+ * @param forward whether to go to the next task in the stack (along the curve) or the previous
* @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
* if the currently focused task is not a stack task, will set the focus
* to the first visible stack task
@@ -667,6 +693,23 @@
* focus.
*/
public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
+ setRelativeFocusedTask(forward, stackTasksOnly, animated, false);
+ }
+
+ /**
+ * Sets the focused task relative to the currently focused task.
+ *
+ * @param forward whether to go to the next task in the stack (along the curve) or the previous
+ * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+ * if the currently focused task is not a stack task, will set the focus
+ * to the first visible stack task
+ * @param animated determines whether to actually draw the highlight along with the change in
+ * focus.
+ * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
+ * happens
+ */
+ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
+ boolean cancelWindowAnimations) {
int newIndex = -1;
if (mFocusedTaskIndex != -1) {
if (stackTasksOnly) {
@@ -690,8 +733,10 @@
}
}
} else {
- // No restrictions, lets just move to the new task
- newIndex = mFocusedTaskIndex + (forward ? -1 : 1);
+ // No restrictions, lets just move to the new task (looping forward/backwards if
+ // necessary)
+ int taskCount = mStack.getTaskCount();
+ newIndex = (mFocusedTaskIndex + (forward ? -1 : 1) + taskCount) % taskCount;
}
} else {
// We don't have a focused task, so focus the first visible task view
@@ -701,7 +746,12 @@
}
}
if (newIndex != -1) {
- setFocusedTask(newIndex, true, animated);
+ boolean willScroll = setFocusedTask(newIndex, true, animated);
+ if (willScroll && cancelWindowAnimations) {
+ // As we iterate to the next/previous task, cancel any current/lagging window
+ // transition animations
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+ }
}
}
@@ -862,10 +912,12 @@
}
// Measure each of the TaskViews
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
+ mTmpTaskViews.clear();
+ mTmpTaskViews.addAll(getTaskViews());
+ mTmpTaskViews.addAll(mViewPool.getViews());
+ int taskViewCount = mTmpTaskViews.size();
for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
+ TaskView tv = mTmpTaskViews.get(i);
if (tv.getBackground() != null) {
tv.getBackground().getPadding(mTmpRect);
} else {
@@ -891,10 +943,12 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Layout each of the TaskViews
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
+ mTmpTaskViews.clear();
+ mTmpTaskViews.addAll(getTaskViews());
+ mTmpTaskViews.addAll(mViewPool.getViews());
+ int taskViewCount = mTmpTaskViews.size();
for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
+ TaskView tv = mTmpTaskViews.get(i);
if (tv.getBackground() != null) {
tv.getBackground().getPadding(mTmpRect);
} else {
@@ -911,6 +965,9 @@
}
if (changed) {
+ if (mStackScroller.isScrollOutOfBounds()) {
+ mStackScroller.boundScroll();
+ }
requestSynchronizeStackViewsWithModel();
synchronizeStackViewsWithModel();
clipTaskViews(true /* forceUpdate */);
@@ -930,9 +987,15 @@
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
- boolean occludesLaunchTarget = (launchTargetTask != null) &&
- launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
- tv.prepareEnterRecentsAnimation(task.isLaunchTarget, occludesLaunchTarget,
+ boolean hideTask = false;
+ boolean occludesLaunchTarget = false;
+ if (launchTargetTask != null) {
+ occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
+ launchTargetTask);
+ hideTask = SystemServicesProxy.isFreeformStack(launchTargetTask.key.stackId) &&
+ SystemServicesProxy.isFreeformStack(task.key.stackId);
+ }
+ tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
offscreenY);
}
@@ -1027,20 +1090,6 @@
}
}
- /** Requests this task stack to start it's dismiss-all animation. */
- public void startDismissAllAnimation(final Runnable postAnimationRunnable) {
- // Clear the focused task
- resetFocusedTask();
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- int count = 0;
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- tv.startDeleteTaskAnimation(i > 0 ? null : postAnimationRunnable, count * 50);
- count++;
- }
- }
-
/** Animates a task view in this stack as it launches. */
public void startLaunchTaskAnimation(TaskView tv, Runnable r, boolean lockToTask) {
Task launchTargetTask = tv.getTask();
@@ -1068,8 +1117,11 @@
mLayersDisabled = false;
// Draw the freeform workspace background
- if (mFreeformWorkspaceBackground.getAlpha() > 0) {
- mFreeformWorkspaceBackground.draw(canvas);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ if (mFreeformWorkspaceBackground.getAlpha() > 0) {
+ mFreeformWorkspaceBackground.draw(canvas);
+ }
}
super.dispatchDraw(canvas);
@@ -1113,8 +1165,8 @@
mViewPool.returnViewToPool(tv);
}
- // Get the stack scroll of the task to anchor to (since we are removing something, the front
- // most task will be our anchor task)
+ // Get the stack scroll of the task to anchor to (since we are removing something, the
+ // front most task will be our anchor task)
Task anchorTask = null;
float prevAnchorTaskScroll = 0;
boolean pullStackForward = stack.getTaskCount() > 0;
@@ -1131,11 +1183,16 @@
// to ensure that the new front most task is now fully visible
mStackScroller.setStackScroll(mLayoutAlgorithm.mMaxScrollP);
} else if (pullStackForward) {
- // Otherwise, offset the scroll by half the movement of the anchor task to allow the
- // tasks behind the removed task to move forward, and the tasks in front to move back
+ // Otherwise, offset the scroll by the movement of the anchor task
float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
- mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
- - prevAnchorTaskScroll) / 2);
+ float newStackScroll = mStackScroller.getStackScroll() +
+ (anchorTaskScroll - prevAnchorTaskScroll);
+ if (mLayoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED) {
+ // If we are focused, we don't want the front task to move, but otherwise, we
+ // allow the back task to move up, and the front task to move back
+ newStackScroll /= 2;
+ }
+ mStackScroller.setStackScroll(newStackScroll);
mStackScroller.boundScroll();
}
@@ -1161,104 +1218,20 @@
TaskView frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
- frontTv.fadeInActionButton(0, getResources().getInteger(
+ frontTv.fadeInActionButton(getResources().getInteger(
R.integer.recents_task_enter_from_app_duration));
}
}
- // If there are no remaining tasks, then either unfilter the current stack, or just close
- // the activity if there are no filtered stacks
+ // If there are no remaining tasks, then just close recents
if (mStack.getTaskCount() == 0) {
- boolean shouldFinishActivity = true;
- if (mStack.hasFilteredTasks()) {
- mStack.unfilterTasks();
- shouldFinishActivity = (mStack.getTaskCount() == 0);
- }
+ boolean shouldFinishActivity = (mStack.getTaskCount() == 0);
if (shouldFinishActivity) {
- mCb.onAllTaskViewsDismissed(null);
+ EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
}
}
}
- @Override
- public void onStackAllTasksRemoved(TaskStack stack, final ArrayList<Task> removedTasks) {
- // Announce for accessibility
- String msg = getContext().getString(R.string.accessibility_recents_all_items_dismissed);
- announceForAccessibility(msg);
-
- startDismissAllAnimation(new Runnable() {
- @Override
- public void run() {
- // Notify that all tasks have been removed
- mCb.onAllTaskViewsDismissed(removedTasks);
- }
- });
- }
-
- @Override
- public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
- Task filteredTask) {
- /*
- // Stash the scroll and filtered task for us to restore to when we unfilter
- mStashedScroll = getStackScroll();
-
- // Calculate the current task transforms
- ArrayList<TaskViewTransform> curTaskTransforms =
- getStackTransforms(curTasks, getStackScroll(), null, true);
-
- // Update the task offsets
- mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
-
- // Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
- updateLayout(false);
- float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight();
- setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
- boundScrollRaw();
-
- // Compute the transforms of the items in the new stack after setting the new scroll
- final ArrayList<Task> tasks = mStack.getTasks();
- final ArrayList<TaskViewTransform> taskTransforms =
- getStackTransforms(mStack.getTasks(), getStackScroll(), null, true);
-
- // Animate
- mFilterAlgorithm.startFilteringAnimation(curTasks, curTaskTransforms, tasks, taskTransforms);
-
- // Notify any callbacks
- mCb.onTaskStackFilterTriggered();
- */
- }
-
- @Override
- public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
- /*
- // Calculate the current task transforms
- final ArrayList<TaskViewTransform> curTaskTransforms =
- getStackTransforms(curTasks, getStackScroll(), null, true);
-
- // Update the task offsets
- mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
-
- // Restore the stashed scroll
- updateLayout(false);
- setStackScrollRaw(mStashedScroll);
- boundScrollRaw();
-
- // Compute the transforms of the items in the new stack after restoring the stashed scroll
- final ArrayList<Task> tasks = mStack.getTasks();
- final ArrayList<TaskViewTransform> taskTransforms =
- getStackTransforms(tasks, getStackScroll(), null, true);
-
- // Animate
- mFilterAlgorithm.startFilteringAnimation(curTasks, curTaskTransforms, tasks, taskTransforms);
-
- // Clear the saved vars
- mStashedScroll = 0;
-
- // Notify any callbacks
- mCb.onTaskStackUnfilterTriggered();
- */
- }
-
/**** ViewPoolConsumer Implementation ****/
@Override
@@ -1346,8 +1319,7 @@
mUIDozeTrigger.stopDozing();
if (mCb != null) {
- mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, false, null,
- INVALID_STACK_ID);
+ mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, null, INVALID_STACK_ID);
}
}
@@ -1500,6 +1472,30 @@
requestSynchronizeStackViewsWithModel(175);
}
+ public final void onBusEvent(StackViewScrolledEvent event) {
+ mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement);
+ }
+
+ public final void onBusEvent(IterateRecentsEvent event) {
+ mLayoutAlgorithm.animateFocusState(mLayoutAlgorithm.getDefaultFocusState());
+ }
+
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+ mEnterAnimationComplete = true;
+ }
+
+ public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+ if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+ tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+ }
+
/**
* Removes the task from the stack, and updates the focus to the next task in the stack if the
* removed TaskView was focused.
@@ -1519,16 +1515,5 @@
// Remove the task from the stack
mStack.removeTask(task);
-
- if (taskWasFocused || ssp.isTouchExplorationEnabled()) {
- // If the dismissed task was focused or if we are in touch exploration mode, then focus
- // the next task
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- boolean isFreeformTask = taskIndex > 0 ?
- mStack.getTasks().get(taskIndex - 1).isFreeformTask() : false;
- setFocusedTask(taskIndex - 1, !isFreeformTask /* scrollToTask */,
- launchState.launchedWithAltTab);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
deleted file mode 100644
index 45f573d..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ /dev/null
@@ -1,176 +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.recents.views;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.model.Task;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/* The layout logic for a TaskStackView */
-public class TaskStackViewFilterAlgorithm {
-
- TaskStackView mStackView;
- ViewPool<TaskView, Task> mViewPool;
-
- public TaskStackViewFilterAlgorithm(TaskStackView stackView, ViewPool<TaskView, Task> viewPool) {
- mStackView = stackView;
- mViewPool = viewPool;
- }
-
- /** Orchestrates the animations of the current child views and any new views. */
- void startFilteringAnimation(ArrayList<Task> curTasks,
- ArrayList<TaskViewTransform> curTaskTransforms,
- final ArrayList<Task> tasks,
- final ArrayList<TaskViewTransform> taskTransforms) {
- // Calculate the transforms to animate out all the existing views if they are not in the
- // new visible range (or to their final positions in the stack if they are)
- final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
- final HashMap<TaskView, TaskViewTransform> childViewTransforms =
- new HashMap<TaskView, TaskViewTransform>();
- int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
- taskTransforms, childViewTransforms, childrenToRemove);
-
- // If all the current views are in the visible range of the new stack, then don't wait for
- // views to animate out and animate all the new views into their place
- final boolean unifyNewViewAnimation = childrenToRemove.isEmpty();
- if (unifyNewViewAnimation) {
- int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms,
- childViewTransforms);
- duration = Math.max(duration, inDuration);
- }
-
- // Animate all the views to their final transforms
- for (final TaskView tv : childViewTransforms.keySet()) {
- TaskViewTransform t = childViewTransforms.get(tv);
- tv.animate().cancel();
- tv.animate()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- childViewTransforms.remove(tv);
- if (childViewTransforms.isEmpty()) {
- // Return all the removed children to the view pool
- for (TaskView tv : childrenToRemove) {
- mViewPool.returnViewToPool(tv);
- }
-
- if (!unifyNewViewAnimation) {
- // For views that are not already visible, animate them in
- childViewTransforms.clear();
- int duration = getEnterTransformsForFilterAnimation(tasks,
- taskTransforms, childViewTransforms);
- for (final TaskView tv : childViewTransforms.keySet()) {
- TaskViewTransform t = childViewTransforms.get(tv);
- tv.updateViewPropertiesToTaskTransform(t, duration);
- }
- }
- }
- }
- });
- tv.updateViewPropertiesToTaskTransform(t, duration);
- }
- }
-
- /**
- * Creates the animations for all the children views that need to be animated in when we are
- * un/filtering a stack, and returns the duration for these animations.
- */
- int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
- ArrayList<TaskViewTransform> taskTransforms,
- HashMap<TaskView, TaskViewTransform> childViewTransformsOut) {
- int offset = 0;
- int movement = 0;
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task task = tasks.get(i);
- TaskViewTransform toTransform = taskTransforms.get(i);
- if (toTransform.visible) {
- TaskView tv = mStackView.getChildViewForTask(task);
- if (tv == null) {
- // For views that are not already visible, animate them in
- tv = mViewPool.pickUpViewFromPool(task, task);
-
- // Compose a new transform to fade and slide the new task in
- TaskViewTransform fromTransform = new TaskViewTransform(toTransform);
- tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
- tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
-
- toTransform.startDelay = offset * 25;
- childViewTransformsOut.put(tv, toTransform);
-
- // Use the movement of the new views to calculate the duration of the animation
- movement = Math.max(movement,
- Math.abs(toTransform.translationY - fromTransform.translationY));
- offset++;
- }
- }
- }
- return mStackView.getResources().getInteger(
- R.integer.recents_filter_animate_new_views_duration);
- }
-
- /**
- * Creates the animations for all the children views that need to be removed or to move views
- * to their un/filtered position when we are un/filtering a stack, and returns the duration
- * for these animations.
- */
- int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks,
- ArrayList<TaskViewTransform> curTaskTransforms,
- ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
- HashMap<TaskView, TaskViewTransform> childViewTransformsOut,
- ArrayList<TaskView> childrenToRemoveOut) {
- // Animate all of the existing views out of view (if they are not in the visible range in
- // the new stack) or to their final positions in the new stack
- int offset = 0;
- int movement = 0;
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
- int taskIndex = tasks.indexOf(task);
- TaskViewTransform toTransform;
-
- // If the view is no longer visible, then we should just animate it out
- boolean willBeInvisible = taskIndex < 0 || !taskTransforms.get(taskIndex).visible;
- if (willBeInvisible) {
- if (taskIndex < 0) {
- toTransform = curTaskTransforms.get(curTasks.indexOf(task));
- } else {
- toTransform = new TaskViewTransform(taskTransforms.get(taskIndex));
- }
- tv.prepareTaskTransformForFilterTaskVisible(toTransform);
- childrenToRemoveOut.add(tv);
- } else {
- toTransform = taskTransforms.get(taskIndex);
- // Use the movement of the visible views to calculate the duration of the animation
- movement = Math.max(movement, Math.abs(toTransform.translationY -
- (int) tv.getTranslationY()));
- }
-
- toTransform.startDelay = offset * 25;
- childViewTransformsOut.put(tv, toTransform);
- offset++;
- }
- return mStackView.getResources().getInteger(
- R.integer.recents_filter_animate_current_views_duration);
- }
-
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 3a2ed0f..62b640e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -166,12 +166,6 @@
mScrollAnimator.setDuration(mContext.getResources().getInteger(
R.integer.recents_animate_task_stack_scroll_duration));
mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
- mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setStackScroll((Float) animation.getAnimatedValue());
- }
- });
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 81c89a1..907ed2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -31,10 +31,10 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -59,6 +59,7 @@
boolean mIsScrolling;
float mDownScrollP;
int mDownX, mDownY;
+ int mLastY;
int mActivePointerId = INACTIVE_POINTER_ID;
int mOverscrollSize;
TaskView mActiveTaskView = null;
@@ -150,11 +151,6 @@
if (mSv.getTaskViews().size() == 0) {
return false;
}
- // Short circuit while we are alt-tabbing
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchState.launchedWithAltTab) {
- return false;
- }
final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
int action = ev.getAction();
@@ -163,6 +159,7 @@
// Save the touch down info
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
+ mLastY = mDownY;
mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(0);
mActiveTaskView = findViewAtPoint(mDownX, mDownY);
@@ -181,6 +178,7 @@
final int index = ev.getActionIndex();
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
+ mLastY = mDownY;
mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(index);
mVelocityTracker.addMovement(ev);
@@ -209,8 +207,10 @@
if (DEBUG) {
Log.d(TAG, "scroll: " + curScrollP);
}
+ EventBus.getDefault().send(new StackViewScrolledEvent(y - mLastY));
}
+ mLastY = y;
mVelocityTracker.addMovement(ev);
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 4f4b91a..523f84f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -29,7 +30,6 @@
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -40,7 +40,6 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
@@ -154,7 +153,7 @@
}
/** Gets the task */
- Task getTask() {
+ public Task getTask() {
return mTask;
}
@@ -199,7 +198,7 @@
// Measure the content
mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
// Measure the bar view, and action button
mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
@@ -210,7 +209,7 @@
// Measure the thumbnail to be square
mThumbnailView.measure(
MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
mThumbnailView.updateClipToTaskBar(mHeaderView);
setMeasuredDimension(width, height);
invalidateOutline();
@@ -252,6 +251,7 @@
mActionButtonView.setAlpha(1f);
mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
}
+ setVisibility(View.VISIBLE);
}
/**
@@ -276,12 +276,14 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
- boolean occludesLaunchTarget, int offscreenY) {
+ void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean hideTask,
+ boolean occludesLaunchTarget, int offscreenY) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
int initialDim = getDim();
- if (launchState.launchedHasConfigurationChanged) {
+ if (hideTask) {
+ setVisibility(View.INVISIBLE);
+ } else if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
} else if (launchState.launchedFromAppWithThumbnail) {
if (isTaskViewLaunchTargetTask) {
@@ -313,10 +315,6 @@
RecentsActivityLaunchState launchState = config.getLaunchState();
Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
- final int transitionEnterFromAppDelay = res.getInteger(
- R.integer.recents_enter_from_app_transition_duration);
- final int transitionEnterFromHomeDelay = res.getInteger(
- R.integer.recents_enter_from_home_transition_duration);
final int taskViewEnterFromAppDuration = res.getInteger(
R.integer.recents_task_enter_from_app_duration);
final int taskViewEnterFromHomeDuration = res.getInteger(
@@ -328,29 +326,13 @@
if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
- // Animate the dim/overlay
- if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
- // Animate the thumbnail alpha before the dim animation (to prevent updating the
- // hardware layer)
- mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay,
- new Runnable() {
- @Override
- public void run() {
- animateDimToProgress(0, taskViewEnterFromAppDuration,
- ctx.postAnimationTrigger.decrementOnAnimationEnd());
- }
- });
- } else {
- // Immediately start the dim animation
- animateDimToProgress(transitionEnterFromAppDelay,
- taskViewEnterFromAppDuration,
- ctx.postAnimationTrigger.decrementOnAnimationEnd());
- }
+ // Immediately start the dim animation
+ animateDimToProgress(taskViewEnterFromAppDuration,
+ ctx.postAnimationTrigger.decrementOnAnimationEnd());
ctx.postAnimationTrigger.increment();
// Animate the action button in
- fadeInActionButton(transitionEnterFromAppDelay,
- taskViewEnterFromAppDuration);
+ fadeInActionButton(taskViewEnterFromAppDuration);
} else {
// Animate the task up if it was occluding the launch target
if (ctx.currentTaskOccludesLaunchTarget) {
@@ -358,17 +340,22 @@
setAlpha(0f);
animate().alpha(1f)
.translationY(transform.translationY)
- .setStartDelay(transitionEnterFromAppDelay)
.setUpdateListener(null)
- .setInterpolator(mFastOutSlowInInterpolator)
- .setDuration(taskViewEnterFromHomeDuration)
- .withEndAction(new Runnable() {
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean hasEnded;
+
+ // We use the animation listener instead of withEndAction() to
+ // ensure that onAnimationEnd() is called when the animator is
+ // cancelled
@Override
- public void run() {
- // Decrement the post animation trigger
+ public void onAnimationEnd(Animator animation) {
+ if (hasEnded) return;
ctx.postAnimationTrigger.decrement();
+ hasEnded = true;
}
})
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .setDuration(taskViewEnterFromHomeDuration)
.start();
ctx.postAnimationTrigger.increment();
}
@@ -377,8 +364,7 @@
} else if (launchState.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
- int delay = transitionEnterFromHomeDelay +
- frontIndex * taskViewEnterFromHomeStaggerDelay;
+ int delay = frontIndex * taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
@@ -389,28 +375,36 @@
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean hasEnded;
+
+ // We use the animation listener instead of withEndAction() to ensure that
+ // onAnimationEnd() is called when the animator is cancelled
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (hasEnded) return;
+ ctx.postAnimationTrigger.decrement();
+ hasEnded = true;
+ }
+ })
.setInterpolator(mQuintOutInterpolator)
.setDuration(taskViewEnterFromHomeDuration +
frontIndex * taskViewEnterFromHomeStaggerDelay)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- // Decrement the post animation trigger
- ctx.postAnimationTrigger.decrement();
- }
- })
.start();
ctx.postAnimationTrigger.increment();
}
}
- public void fadeInActionButton(int delay, int duration) {
+ public void cancelEnterRecentsAnimation() {
+ animate().cancel();
+ }
+
+ public void fadeInActionButton(int duration) {
// Hide the action button
mActionButtonView.setAlpha(0f);
// Animate the action button in
mActionButtonView.animate().alpha(1f)
- .setStartDelay(delay)
.setDuration(duration)
.setInterpolator(PhoneStatusBar.ALPHA_IN)
.start();
@@ -431,11 +425,6 @@
ctx.postAnimationTrigger.increment();
}
- /** Animates this task view away when dismissing all tasks. */
- void startDismissAllAnimation() {
- dismissTask();
- }
-
/** Animates this task view as it exits recents */
void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
boolean occludesLaunchTarget, boolean lockToTask) {
@@ -611,12 +600,11 @@
}
/** Animates the dim to the task progress. */
- void animateDimToProgress(int delay, int duration, Animator.AnimatorListener postAnimRunnable) {
+ void animateDimToProgress(int duration, Animator.AnimatorListener postAnimRunnable) {
// Animate the dim into view as well
int toDim = getDimFromTaskProgress();
if (toDim != getDim()) {
ObjectAnimator anim = ObjectAnimator.ofInt(TaskView.this, "dim", toDim);
- anim.setStartDelay(delay);
anim.setDuration(duration);
if (postAnimRunnable != null) {
anim.addListener(postAnimRunnable);
@@ -642,21 +630,6 @@
setDim(getDimFromTaskProgress());
}
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (Constants.DebugFlags.App.EnableFastToggleRecents && mIsFocused) {
- Paint tmpPaint = new Paint();
- Rect tmpRect = new Rect();
- tmpRect.set(0, 0, getWidth(), getHeight());
- tmpPaint.setColor(0xFFFF0000);
- tmpPaint.setStrokeWidth(35);
- tmpPaint.setStyle(Paint.Style.STROKE);
- canvas.drawRect(tmpRect, tmpPaint);
- }
- }
-
/**** View focus state ****/
/**
@@ -687,9 +660,6 @@
clearAccessibilityFocus();
}
}
- if (Constants.DebugFlags.App.EnableFastToggleRecents) {
- invalidate();
- }
}
/**
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 649199e..76c6691 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -16,12 +16,7 @@
package com.android.systemui.recents.views;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
@@ -69,16 +64,13 @@
TextView mActivityDescription;
// Header drawables
- boolean mCurrentPrimaryColorIsDark;
- int mCurrentPrimaryColor;
- int mBackgroundColor;
int mCornerRadius;
int mHighlightHeight;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
RippleDrawable mBackground;
GradientDrawable mBackgroundColorDrawable;
- AnimatorSet mFocusAnimator;
+ ObjectAnimator mFocusAnimator;
String mDismissContentDescription;
// Static highlight that we draw at the top of each view
@@ -223,15 +215,12 @@
((ColorDrawable) getBackground()).getColor() : 0;
if (existingBgColor != t.colorPrimary) {
mBackgroundColorDrawable.setColor(t.colorPrimary);
- mBackgroundColor = t.colorPrimary;
}
int taskBarViewLightTextColor = getResources().getColor(
R.color.recents_task_bar_light_text_color);
int taskBarViewDarkTextColor = getResources().getColor(
R.color.recents_task_bar_dark_text_color);
- mCurrentPrimaryColor = t.colorPrimary;
- mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
taskBarViewLightTextColor : taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
@@ -258,7 +247,6 @@
// Stop any focus animations
Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
- mBackground.jumpToCurrentState();
}
/** Updates the resize task bar button. */
@@ -376,84 +364,22 @@
isRunning = mFocusAnimator.isRunning();
}
Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
- mBackground.jumpToCurrentState();
if (focused) {
// If we are not animating the visible state, just return
if (!animateFocusedState) return;
- int currentColor = mBackgroundColor;
- int secondaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
- int[][] states = new int[][] {
- new int[] {},
- new int[] { android.R.attr.state_enabled },
- new int[] { android.R.attr.state_pressed }
- };
- int[] newStates = new int[]{
- 0,
- android.R.attr.state_enabled,
- android.R.attr.state_pressed
- };
- int[] colors = new int[] {
- currentColor,
- secondaryColor,
- secondaryColor
- };
- mBackground.setColor(new ColorStateList(states, colors));
- mBackground.setState(newStates);
- // Pulse the background color
- int lightPrimaryColor = getSecondaryColor(mCurrentPrimaryColor, mCurrentPrimaryColorIsDark);
- ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
- currentColor, lightPrimaryColor);
- backgroundColor.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackground.setState(new int[]{});
- }
- });
- backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int color = (int) animation.getAnimatedValue();
- mBackgroundColorDrawable.setColor(color);
- mBackgroundColor = color;
- }
- });
- backgroundColor.setRepeatCount(ValueAnimator.INFINITE);
- backgroundColor.setRepeatMode(ValueAnimator.REVERSE);
- // Pulse the translation
- ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 15f);
- translation.setRepeatCount(ValueAnimator.INFINITE);
- translation.setRepeatMode(ValueAnimator.REVERSE);
-
- mFocusAnimator = new AnimatorSet();
- mFocusAnimator.playTogether(backgroundColor, translation);
- mFocusAnimator.setStartDelay(150);
- mFocusAnimator.setDuration(750);
+ // Bump up the translation
+ mFocusAnimator = ObjectAnimator.ofFloat(this, "translationZ", 8f);
+ mFocusAnimator.setDuration(200);
mFocusAnimator.start();
} else {
if (isRunning) {
- // Restore the background color
- int currentColor = mBackgroundColor;
- ValueAnimator backgroundColor = ValueAnimator.ofObject(new ArgbEvaluator(),
- currentColor, mCurrentPrimaryColor);
- backgroundColor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int color = (int) animation.getAnimatedValue();
- mBackgroundColorDrawable.setColor(color);
- mBackgroundColor = color;
- }
- });
// Restore the translation
- ObjectAnimator translation = ObjectAnimator.ofFloat(this, "translationZ", 0f);
-
- mFocusAnimator = new AnimatorSet();
- mFocusAnimator.playTogether(backgroundColor, translation);
+ mFocusAnimator = ObjectAnimator.ofFloat(this, "translationZ", 0f);
mFocusAnimator.setDuration(150);
mFocusAnimator.start();
} else {
- mBackground.setState(new int[] {});
setTranslationZ(0f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 690c297..bc50846 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -218,13 +218,13 @@
void onFocusChanged(boolean focused) {
if (focused) {
if (Float.compare(getAlpha(), 1f) != 0) {
- startFadeAnimation(1f, 0, 150, null);
+ startFadeAnimation(1f, 150, null);
}
} else {
float taskViewThumbnailAlpha = getResources().getFloat(
R.dimen.recents_task_view_thumbnail_alpha);
if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
- startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null);
+ startFadeAnimation(taskViewThumbnailAlpha, 150, null);
}
}
}
@@ -244,10 +244,10 @@
}
/** Animates this task thumbnail as it enters Recents. */
- void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
+ void startEnterRecentsAnimation(Runnable postAnimRunnable) {
float taskViewThumbnailAlpha = getResources().getFloat(
R.dimen.recents_task_view_thumbnail_alpha);
- startFadeAnimation(taskViewThumbnailAlpha, delay,
+ startFadeAnimation(taskViewThumbnailAlpha,
getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
postAnimRunnable);
}
@@ -256,14 +256,13 @@
void startLaunchTaskAnimation(Runnable postAnimRunnable) {
int taskViewExitToAppDuration = mContext.getResources().getInteger(
R.integer.recents_task_exit_to_app_duration);
- startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable);
+ startFadeAnimation(1f, taskViewExitToAppDuration, postAnimRunnable);
}
/** Starts a new thumbnail alpha animation. */
- void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) {
+ void startFadeAnimation(float finalAlpha, int duration, final Runnable postAnimRunnable) {
Utilities.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator);
mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
- mThumbnailAlphaAnimator.setStartDelay(delay);
mThumbnailAlphaAnimator.setDuration(duration);
mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
index 12b91af..31fbd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
@@ -20,6 +20,7 @@
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
/* A view pool to manage more views than we can visibly handle */
@@ -76,11 +77,10 @@
return v;
}
- /** Returns an iterator to the list of the views in the pool. */
- Iterator<V> poolViewIterator() {
- if (mPool != null) {
- return mPool.iterator();
- }
- return null;
+ /**
+ * Returns the list of views in the pool.
+ */
+ List<V> getViews() {
+ return mPool;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9e3cf37..e6a291c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -594,7 +594,9 @@
// Setup the animation with the screenshot just taken
if (mScreenshotAnimation != null) {
- mScreenshotAnimation.end();
+ if (mScreenshotAnimation.isStarted()) {
+ mScreenshotAnimation.end();
+ }
mScreenshotAnimation.removeAllListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index dd894ce..50e010f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -32,6 +32,7 @@
private static final String TAG = "Divider";
private int mDividerWindowWidth;
private DividerWindowManager mWindowManager;
+ private DividerView mView;
@Override
public void start() {
@@ -39,6 +40,7 @@
mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
update(mContext.getResources().getConfiguration());
+ putComponent(Divider.class, this);
}
@Override
@@ -47,14 +49,18 @@
update(newConfig);
}
+ public DividerView getView() {
+ return mView;
+ }
+
private void addDivider(Configuration configuration) {
- DividerView view = (DividerView)
+ mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
- mWindowManager.add(view, width, height);
- view.setWindowManager(mWindowManager);
+ mWindowManager.add(mView, width, height);
+ mView.setWindowManager(mWindowManager);
}
private void removeDivider() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
index 5f983c5..69e90cc 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
@@ -98,10 +98,10 @@
// TODO: Better calculation
targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START));
- targets.add(new SnapTarget((int) (0.35f * dividerMax) - mDividerSize / 2,
+ targets.add(new SnapTarget((int) (0.38f * dividerMax) - mDividerSize / 2,
SnapTarget.FLAG_NONE));
targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE));
- targets.add(new SnapTarget((int) (0.65f * dividerMax) - mDividerSize / 2,
+ targets.add(new SnapTarget((int) (0.62f * dividerMax) - mDividerSize / 2,
SnapTarget.FLAG_NONE));
targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END));
return targets;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 59d4011..98f3f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,8 +25,10 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region.Op;
+import android.hardware.display.DisplayManager;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -125,6 +127,28 @@
mWindowManager = windowManager;
}
+ public WindowManagerProxy getWindowManagerProxy() {
+ return mWindowManagerProxy;
+ }
+
+ public boolean startDragging() {
+ mDockSide = mWindowManagerProxy.getDockSide();
+ if (mDockSide != WindowManager.DOCKED_INVALID) {
+ mWindowManagerProxy.setResizing(true);
+ mWindowManager.setSlippery(false);
+ liftBackground();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void stopDragging(int position, float velocity) {
+ fling(position, velocity);
+ mWindowManager.setSlippery(true);
+ releaseBackground();
+ }
+
@Override
public boolean onTouch(View v, MotionEvent event) {
convertToScreenCoordinates(event);
@@ -136,20 +160,13 @@
mStartX = (int) event.getX();
mStartY = (int) event.getY();
getLocationOnScreen(mTempInt2);
- mDockSide = mWindowManagerProxy.getDockSide();
+ boolean result = startDragging();
if (isHorizontalDivision()) {
mStartPosition = mTempInt2[1] + mDividerInsets;
} else {
mStartPosition = mTempInt2[0] + mDividerInsets;
}
- if (mDockSide != WindowManager.DOCKED_INVALID) {
- mWindowManagerProxy.setResizing(true);
- mWindowManager.setSlippery(false);
- liftBackground();
- return true;
- } else {
- return false;
- }
+ return result;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
int x = (int) event.getX();
@@ -166,10 +183,9 @@
y = (int) event.getRawY();
mVelocityTracker.computeCurrentVelocity(1000);
- fling(x, y, mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-
- mWindowManager.setSlippery(true);
- releaseBackground();
+ int position = calculatePosition(x, y);
+ stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
+ : mVelocityTracker.getXVelocity());
break;
}
return true;
@@ -179,9 +195,7 @@
event.setLocation(event.getRawX(), event.getRawY());
}
- private void fling(int x, int y, float xVelocity, float yVelocity) {
- int position = calculatePosition(x, y);
- float velocity = isHorizontalDivision() ? yVelocity : xVelocity;
+ private void fling(int position, float velocity) {
final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity);
@@ -262,18 +276,21 @@
}
private void updateDisplayInfo() {
- DisplayMetrics info = mContext.getResources().getDisplayMetrics();
- mDisplayWidth = info.widthPixels;
- mDisplayHeight = info.heightPixels;
+ final DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ final DisplayInfo info = new DisplayInfo();
+ display.getDisplayInfo(info);
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
}
private int calculatePosition(int touchX, int touchY) {
return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX);
}
- private boolean isHorizontalDivision() {
- return mDockSide == WindowManager.DOCKED_TOP
- || mDockSide == WindowManager.DOCKED_BOTTOM;
+ public boolean isHorizontalDivision() {
+ return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}
private int calculateXPosition(int touchX) {
@@ -284,22 +301,38 @@
return mStartPosition + touchY - mStartY;
}
- private void resizeStack(int position) {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- switch (mDockSide) {
+ public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+ outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (dockSide) {
case WindowManager.DOCKED_LEFT:
- mTmpRect.right = position;
+ outRect.right = position;
break;
case WindowManager.DOCKED_TOP:
- mTmpRect.bottom = position;
+ outRect.bottom = position;
break;
case WindowManager.DOCKED_RIGHT:
- mTmpRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
+ outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
break;
case WindowManager.DOCKED_BOTTOM:
- mTmpRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
+ outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
break;
}
+ if (outRect.left > outRect.right) {
+ outRect.left = outRect.right;
+ }
+ if (outRect.top > outRect.bottom) {
+ outRect.top = outRect.bottom;
+ }
+ if (outRect.right < outRect.left) {
+ outRect.right = outRect.left;
+ }
+ if (outRect.bottom < outRect.top) {
+ outRect.bottom = outRect.top;
+ }
+ }
+
+ public void resizeStack(int position) {
+ calculateBoundsForPosition(position, mDockSide, mTmpRect);
if (mTmpRect.equals(mLastResizeRect)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 94809bc..0d3f803 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -49,8 +49,29 @@
mTmpRect.set(mResizeRect);
}
try {
- ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID,
- mTmpRect, true);
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, mTmpRect, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mDismissRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mMaximizeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
@@ -65,27 +86,24 @@
}
public void dismissDockedStack() {
- try {
- ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove stack: " + e);
- }
+ mExecutor.execute(mDismissRunnable);
}
public void maximizeDockedStack() {
- try {
- ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
+ mExecutor.execute(mMaximizeRunnable);
}
- public void setResizing(boolean resizing) {
- try {
- WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
- } catch (RemoteException e) {
- Log.w(TAG, "Error calling setDockedStackResizing: " + e);
- }
+ public void setResizing(final boolean resizing) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling setDockedStackResizing: " + e);
+ }
+ }
+ });
}
public int getDockSide() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 3f0000e..723989a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -62,6 +62,7 @@
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,6 +80,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.widget.DateTimeView;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -175,6 +177,7 @@
protected boolean mDeviceInteractive;
protected boolean mVisible;
+ protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
@@ -278,6 +281,10 @@
@Override
public boolean onClickHandler(
final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
+ if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
+ return true;
+ }
+
if (DEBUG) {
Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
}
@@ -366,6 +373,65 @@
Intent fillInIntent) {
return super.onClickHandler(view, pendingIntent, fillInIntent);
}
+
+ private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+ Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
+ RemoteInput[] inputs = null;
+ if (tag instanceof RemoteInput[]) {
+ inputs = (RemoteInput[]) tag;
+ }
+
+ if (inputs == null) {
+ return false;
+ }
+
+ RemoteInput input = null;
+
+ for (RemoteInput i : inputs) {
+ if (i.getAllowFreeFormInput()) {
+ input = i;
+ }
+ }
+
+ if (input == null) {
+ return false;
+ }
+
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.isRootNamespace()) {
+ riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+
+ if (riv == null) {
+ return false;
+ }
+
+ riv.setVisibility(View.VISIBLE);
+ int cx = view.getLeft() + view.getWidth() / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+ ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
+ .start();
+
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(inputs, input);
+ riv.focus();
+
+ return true;
+ }
+
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -468,7 +534,6 @@
processForRemoteInput(sbn.getNotification());
String key = sbn.getKey();
boolean isUpdate = mNotificationData.get(key) != null;
-
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since we're not going to show them
// anyway. This is true also when the summary is canceled,
@@ -1550,15 +1615,15 @@
RemoteInput remoteInput = null;
- // See if the notification has exactly one action and this action allows free-form input
- // TODO: relax restrictions once we support more than one remote input action.
Notification.Action[] actions = entry.notification.getNotification().actions;
- if (actions != null && actions.length == 1) {
- if (actions[0].getRemoteInputs() != null) {
- for (RemoteInput ri : actions[0].getRemoteInputs()) {
- if (ri.getAllowFreeFormInput()) {
- remoteInput = ri;
- break;
+ if (actions != null) {
+ for (Notification.Action a : actions) {
+ if (a.getRemoteInputs() != null) {
+ for (RemoteInput ri : a.getRemoteInputs()) {
+ if (ri.getAllowFreeFormInput()) {
+ remoteInput = ri;
+ break;
+ }
}
}
}
@@ -1568,32 +1633,36 @@
if (remoteInput != null) {
View bigContentView = entry.getExpandedContentView();
if (bigContentView != null) {
- inflateRemoteInput(bigContentView, entry, remoteInput, actions);
+ inflateRemoteInput(bigContentView, entry);
}
View headsUpContentView = entry.getHeadsUpContentView();
if (headsUpContentView != null) {
- inflateRemoteInput(headsUpContentView, entry, remoteInput, actions);
+ inflateRemoteInput(headsUpContentView, entry);
}
}
}
- private void inflateRemoteInput(View view, Entry entry, RemoteInput remoteInput,
- Notification.Action[] actions) {
- View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
- if (actionContainerCandidate instanceof ViewGroup) {
- ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
- RemoteInputView riv = inflateRemoteInputView(actionContainer, entry,
- actions[0], remoteInput);
+ private RemoteInputView inflateRemoteInput(View view, Entry entry) {
+ View actionContainerCandidate = view.findViewById(
+ com.android.internal.R.id.actions_container);
+ if (actionContainerCandidate instanceof FrameLayout) {
+ ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
+ RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
if (riv != null) {
- actionContainer.removeAllViews();
- actionContainer.addView(riv);
+ riv.setVisibility(View.INVISIBLE);
+ actionContainer.addView(riv, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ );
+ riv.setBackgroundColor(entry.notification.getNotification().color);
+ return riv;
}
}
+ return null;
}
- protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
- Notification.Action action, RemoteInput remoteInput) {
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry) {
return null;
}
@@ -1664,7 +1733,10 @@
return;
}
- final PendingIntent intent = sbn.getNotification().contentIntent;
+ Notification notification = sbn.getNotification();
+ final PendingIntent intent = notification.contentIntent != null
+ ? notification.contentIntent
+ : notification.fullScreenIntent;
final String notificationKey = sbn.getKey();
// Mark notification for one frame.
@@ -1746,8 +1818,8 @@
}
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
- final PendingIntent contentIntent = sbn.getNotification().contentIntent;
- if (contentIntent != null) {
+ Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null || notification.fullScreenIntent != null) {
row.setOnClickListener(this);
} else {
row.setOnClickListener(null);
@@ -2013,11 +2085,14 @@
Entry entry = mNotificationData.get(key);
if (entry == null) {
return;
+ } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+ mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
}
Notification n = notification.getNotification();
+ mNotificationData.updateRanking(ranking);
- boolean applyInPlace = !entry.cacheContentViews(mContext, notification.getNotification());
+ boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
boolean shouldInterrupt = shouldInterrupt(entry, notification);
boolean alertAgain = alertAgain(entry, n);
if (DEBUG) {
@@ -2070,7 +2145,6 @@
inflateViews(entry, mStackScroller);
}
updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
- mNotificationData.updateRanking(ranking);
updateNotifications();
// Update the veto button accordingly (and as a result, whether this row is
@@ -2155,20 +2229,17 @@
boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
boolean isFullscreen = notification.fullScreenIntent != null;
boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
- boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
- Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
boolean accessibilityForcesLaunch = isFullscreen
&& mAccessibilityManager.isTouchExplorationEnabled();
boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
-
boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
- && isAllowed
&& !accessibilityForcesLaunch
&& !justLaunchedFullScreenIntent
&& mPowerManager.isScreenOn()
&& (!mStatusBarKeyguardViewManager.isShowing()
|| mStatusBarKeyguardViewManager.isOccluded())
- && !mStatusBarKeyguardViewManager.isInputRestricted();
+ && !mStatusBarKeyguardViewManager.isInputRestricted()
+ && !mNotificationData.shouldSuppressPeek(sbn.getKey());
try {
interrupt = interrupt && !mDreamManager.isDreaming();
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 6a90d8e..83dbde5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -105,36 +105,29 @@
}
public boolean cacheContentViews(Context ctx, Notification updatedNotification) {
- boolean cached = false;
+ boolean applyInPlace = false;
if (updatedNotification != null) {
final Notification.Builder updatedNotificationBuilder
= Notification.Builder.recoverBuilder(ctx, updatedNotification);
final RemoteViews newContentView = updatedNotificationBuilder.makeContentView();
- if (!compareRemoteViews(cachedContentView, newContentView)) {
- cachedContentView = newContentView;
- cached |= true;
- }
final RemoteViews newBigContentView =
updatedNotificationBuilder.makeBigContentView();
- if (!compareRemoteViews(cachedBigContentView, newBigContentView)) {
- cachedBigContentView = newBigContentView;
- cached |= true;
- }
final RemoteViews newHeadsUpContentView =
updatedNotificationBuilder.makeHeadsUpContentView();
- if (!compareRemoteViews(cachedHeadsUpContentView, newBigContentView)) {
- cachedHeadsUpContentView = newHeadsUpContentView;
- cached |= true;
- }
final Notification updatedPublicNotification = updatedNotification.publicVersion;
final RemoteViews newPubContentView = (updatedPublicNotification != null)
? Notification.Builder.recoverBuilder(
ctx, updatedPublicNotification).makeContentView()
: null;
- if (!compareRemoteViews(cachedPublicContentView, newPubContentView)) {
- cachedPublicContentView = newPubContentView;
- cached |= true;
- }
+
+ applyInPlace = compareRemoteViews(cachedContentView, newContentView)
+ && compareRemoteViews(cachedBigContentView, newBigContentView)
+ && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
+ && compareRemoteViews(cachedPublicContentView, newPubContentView);
+ cachedPublicContentView = newPubContentView;
+ cachedHeadsUpContentView = newHeadsUpContentView;
+ cachedBigContentView = newBigContentView;
+ cachedContentView = newContentView;
} else {
final Notification.Builder builder
= Notification.Builder.recoverBuilder(ctx, notification.getNotification());
@@ -150,9 +143,9 @@
= Notification.Builder.recoverBuilder(ctx, publicNotification);
cachedPublicContentView = publicBuilder.makeContentView();
}
- cached = true;
+ applyInPlace = false;
}
- return cached;
+ return applyInPlace;
}
// Returns true if the RemoteViews are the same.
@@ -292,6 +285,15 @@
return NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
}
+ public boolean shouldSuppressPeek(String key) {
+ if (mRankingMap != null) {
+ mRankingMap.getRanking(key, mTmpRanking);
+ return (mTmpRanking.getSuppressedVisualEffects()
+ & NotificationListenerService.SUPPRESSED_EFFECT_PEEK) != 0;
+ }
+ return false;
+ }
+
private void updateRankingAndSort(RankingMap ranking) {
if (ranking != null) {
mRankingMap = ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
new file mode 100644
index 0000000..d91bfb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -0,0 +1,262 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.BaseStatusBar;
+
+import static android.view.WindowManager.*;
+
+/**
+ * Class to detect gestures on the navigation bar.
+ */
+public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureListener {
+
+ private static final String DOCK_WINDOW_GESTURE_ENABLED_PROP = "persist.dock_gesture_enabled";
+
+ /**
+ * When dragging from the navigation bar, we drag in recents.
+ */
+ private 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;
+
+ private RecentsComponent mRecentsComponent;
+ private Divider mDivider;
+ private boolean mIsVertical;
+ private boolean mIsRTL;
+
+ private final GestureDetector mTaskSwitcherDetector;
+ private final int mScrollTouchSlop;
+ private final int mTouchSlop;
+ private final int mMinFlingVelocity;
+ private int mTouchDownX;
+ private int mTouchDownY;
+ private VelocityTracker mVelocityTracker;
+
+ private boolean mDockWindowEnabled;
+ private boolean mDockWindowTouchSlopExceeded;
+ private int mDragMode;
+
+ public NavigationBarGestureHelper(Context context) {
+ ViewConfiguration configuration = ViewConfiguration.get(context);
+ Resources r = context.getResources();
+ mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mTaskSwitcherDetector = new GestureDetector(context, this);
+ mDockWindowEnabled = SystemProperties.getBoolean(DOCK_WINDOW_GESTURE_ENABLED_PROP, false);
+ }
+
+ public void setComponents(RecentsComponent recentsComponent, Divider divider) {
+ mRecentsComponent = recentsComponent;
+ mDivider = divider;
+ }
+
+ public void setBarState(boolean isVertical, boolean isRTL) {
+ mIsVertical = isVertical;
+ mIsRTL = isRTL;
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ // If we move more than a fixed amount, then start capturing for the
+ // task switcher detector
+ mTaskSwitcherDetector.onTouchEvent(event);
+ int action = event.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ mTouchDownX = (int) event.getX();
+ mTouchDownY = (int) event.getY();
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int xDiff = Math.abs(x - mTouchDownX);
+ int yDiff = Math.abs(y - mTouchDownY);
+ boolean exceededTouchSlop = !mIsVertical
+ ? xDiff > mScrollTouchSlop && xDiff > yDiff
+ : yDiff > mScrollTouchSlop && yDiff > xDiff;
+ if (exceededTouchSlop) {
+ return true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ break;
+ }
+ return mDockWindowEnabled && interceptDockWindowEvent(event);
+ }
+
+ private boolean interceptDockWindowEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ handleDragActionDownEvent(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ return handleDragActionMoveEvent(event);
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ handleDragActionUpEvent(event);
+ break;
+ }
+ return false;
+ }
+
+ private boolean handleDockWindowEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ handleDragActionDownEvent(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ handleDragActionMoveEvent(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ handleDragActionUpEvent(event);
+ break;
+ }
+ return true;
+ }
+
+ private void handleDragActionDownEvent(MotionEvent event) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mDockWindowTouchSlopExceeded = false;
+ mTouchDownX = (int) event.getX();
+ mTouchDownY = (int) event.getY();
+ }
+
+ private boolean handleDragActionMoveEvent(MotionEvent event) {
+ mVelocityTracker.addMovement(event);
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int xDiff = Math.abs(x - mTouchDownX);
+ int yDiff = Math.abs(y - mTouchDownY);
+ if (!mDockWindowTouchSlopExceeded) {
+ boolean touchSlopExceeded = !mIsVertical
+ ? yDiff > mTouchSlop && yDiff > xDiff
+ : xDiff > mTouchSlop && xDiff > yDiff;
+ if (touchSlopExceeded && mDivider.getView().getWindowManagerProxy().getDockSide()
+ == DOCKED_INVALID) {
+ mDragMode = calculateDragMode();
+ Rect initialBounds = null;
+ if (mDragMode == DRAG_MODE_DIVIDER) {
+ initialBounds = new Rect();
+ mDivider.getView().calculateBoundsForPosition(mIsVertical
+ ? (int) event.getRawX()
+ : (int) event.getRawY(),
+ mDivider.getView().isHorizontalDivision()
+ ? DOCKED_TOP
+ : DOCKED_LEFT,
+ initialBounds);
+ }
+ mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, initialBounds);
+ if (mDragMode == DRAG_MODE_DIVIDER) {
+ mDivider.getView().startDragging();
+ }
+ mDockWindowTouchSlopExceeded = true;
+ return true;
+ }
+ } else {
+ if (mDragMode == DRAG_MODE_DIVIDER) {
+ mDivider.getView().resizeStack(
+ !mIsVertical ? (int) event.getRawY() : (int) event.getRawX());
+ } else if (mDragMode == DRAG_MODE_RECENTS) {
+ mRecentsComponent.onDraggingInRecents(event.getRawY());
+ }
+ }
+ return false;
+ }
+
+ private void handleDragActionUpEvent(MotionEvent event) {
+ mVelocityTracker.addMovement(event);
+ mVelocityTracker.computeCurrentVelocity(1000);
+ if (mDockWindowTouchSlopExceeded) {
+ if (mDragMode == DRAG_MODE_DIVIDER) {
+ mDivider.getView().stopDragging(mIsVertical
+ ? (int) event.getRawX()
+ : (int) event.getRawY(),
+ mIsVertical
+ ? mVelocityTracker.getXVelocity()
+ : mVelocityTracker.getYVelocity());
+ } else if (mDragMode == DRAG_MODE_RECENTS) {
+ mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
+ }
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ private int calculateDragMode() {
+ if (mIsVertical && !mDivider.getView().isHorizontalDivision()) {
+ return DRAG_MODE_DIVIDER;
+ }
+ if (!mIsVertical && mDivider.getView().isHorizontalDivision()) {
+ return DRAG_MODE_DIVIDER;
+ }
+ return DRAG_MODE_RECENTS;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean result = mTaskSwitcherDetector.onTouchEvent(event);
+ if (mDockWindowEnabled) {
+ result |= handleDockWindowEvent(event);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ float absVelX = Math.abs(velocityX);
+ float absVelY = Math.abs(velocityY);
+ boolean isValidFling = absVelX > mMinFlingVelocity &&
+ mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
+ if (isValidFling) {
+ boolean showNext;
+ if (!mIsRTL) {
+ showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
+ } else {
+ // In RTL, vertical is still the same, but horizontal is flipped
+ showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0);
+ }
+ if (showNext) {
+ mRecentsComponent.showNextAffiliatedTask();
+ } else {
+ mRecentsComponent.showPrevAffiliatedTask();
+ }
+ }
+ return true;
+ }
+}
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 33e514d..e1aec6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,7 +40,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
@@ -48,6 +47,8 @@
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonView;
@@ -78,7 +79,7 @@
private Drawable mRecentIcon;
private Drawable mRecentLandIcon;
- private NavigationBarViewTaskSwitchHelper mTaskSwitchHelper;
+ private NavigationBarGestureHelper mGestureHelper;
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
@@ -180,7 +181,7 @@
mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
mVertical = false;
mShowMenu = false;
- mTaskSwitchHelper = new NavigationBarViewTaskSwitchHelper(context);
+ mGestureHelper = new NavigationBarGestureHelper(context);
getIcons(res);
@@ -191,8 +192,8 @@
return mBarTransitions;
}
- public void setBar(PhoneStatusBar phoneStatusBar) {
- mTaskSwitchHelper.setBar(phoneStatusBar);
+ public void setComponents(RecentsComponent recentsComponent, Divider divider) {
+ mGestureHelper.setComponents(recentsComponent, divider);
}
public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
@@ -202,7 +203,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mTaskSwitchHelper.onTouchEvent(event)) {
+ if (mGestureHelper.onTouchEvent(event)) {
return true;
}
if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
@@ -213,7 +214,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- return mTaskSwitchHelper.onInterceptTouchEvent(event);
+ return mGestureHelper.onInterceptTouchEvent(event);
}
public void abortCurrentGesture() {
@@ -488,7 +489,7 @@
private void updateTaskSwitchHelper() {
boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
- mTaskSwitchHelper.setBarState(mVertical, isRtl);
+ mGestureHelper.setBarState(mVertical, isRtl);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
deleted file mode 100644
index fdfcdfb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
+++ /dev/null
@@ -1,114 +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.phone;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
-
-public class NavigationBarViewTaskSwitchHelper extends GestureDetector.SimpleOnGestureListener {
-
- private BaseStatusBar mBar;
- private boolean mIsVertical;
- private boolean mIsRTL;
-
- private final GestureDetector mTaskSwitcherDetector;
- private final int mScrollTouchSlop;
- private final int mMinFlingVelocity;
- private int mTouchDownX;
- private int mTouchDownY;
-
- public NavigationBarViewTaskSwitchHelper(Context context) {
- ViewConfiguration configuration = ViewConfiguration.get(context);
- Resources r = context.getResources();
- mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
- mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
- mTaskSwitcherDetector = new GestureDetector(context, this);
- }
-
- public void setBar(BaseStatusBar phoneStatusBar) {
- mBar = phoneStatusBar;
- }
-
- public void setBarState(boolean isVertical, boolean isRTL) {
- mIsVertical = isVertical;
- mIsRTL = isRTL;
- }
-
- public boolean onInterceptTouchEvent(MotionEvent event) {
- // If we move more than a fixed amount, then start capturing for the
- // task switcher detector
- mTaskSwitcherDetector.onTouchEvent(event);
- int action = event.getAction();
- boolean intercepted = false;
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- mTouchDownX = (int) event.getX();
- mTouchDownY = (int) event.getY();
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- int x = (int) event.getX();
- int y = (int) event.getY();
- int xDiff = Math.abs(x - mTouchDownX);
- int yDiff = Math.abs(y - mTouchDownY);
- boolean exceededTouchSlop = !mIsVertical
- ? xDiff > mScrollTouchSlop && xDiff > yDiff
- : yDiff > mScrollTouchSlop && yDiff > xDiff;
- if (exceededTouchSlop) {
- return true;
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- break;
- }
- return intercepted;
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- return mTaskSwitcherDetector.onTouchEvent(event);
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- float absVelX = Math.abs(velocityX);
- float absVelY = Math.abs(velocityY);
- boolean isValidFling = absVelX > mMinFlingVelocity &&
- mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
- if (isValidFling) {
- boolean showNext;
- if (!mIsRTL) {
- showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
- } else {
- // In RTL, vertical is still the same, but horizontal is flipped
- showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0);
- }
- if (showNext) {
- mBar.showNextAffiliatedTask();
- } else {
- mBar.showPreviousAffiliatedTask();
- }
- }
- return true;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index bbef1c0..fbe9730 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -84,10 +84,10 @@
// the close future. See b/23676310 for reference.
return;
}
- if (notif.isGroupSummary()) {
- group.summary = null;
- } else {
+ if (notif.isGroupChild()) {
group.children.remove(removed);
+ } else {
+ group.summary = null;
}
if (group.children.isEmpty()) {
if (group.summary == null) {
@@ -107,17 +107,17 @@
group = new NotificationGroup();
mGroupMap.put(groupKey, group);
}
- if (notif.isGroupSummary()) {
+ if (notif.isGroupChild()) {
+ group.children.add(added);
+ if (group.summary != null && group.children.size() == 1 && !group.expanded) {
+ group.summary.row.updateNotificationHeader();
+ }
+ } else {
group.summary = added;
group.expanded = added.row.areChildrenExpanded();
if (!group.children.isEmpty()) {
mListener.onGroupCreatedFromChildren(group);
}
- } else {
- group.children.add(added);
- if (group.summary != null && group.children.size() == 1 && !group.expanded) {
- group.summary.row.updateNotificationHeader();
- }
}
}
@@ -169,7 +169,7 @@
* @return whether a given notification is a summary in a group which has children
*/
public boolean isSummaryOfGroup(StatusBarNotification sbn) {
- if (sbn.getNotification().isGroupChild()) {
+ if (!sbn.getNotification().isGroupSummary()) {
return false;
}
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
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 3e52515..6d8e650 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -117,6 +117,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -617,7 +618,6 @@
};
private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
= new HashMap<>();
- private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
private RankingMap mLatestRankingMap;
private boolean mNoAnimationOnNextBarModeChange;
private FalsingManager mFalsingManager;
@@ -740,7 +740,7 @@
context, R.layout.navigation_bar, null);
}
mNavigationBarView.setDisabledFlags(mDisabled1);
- mNavigationBarView.setBar(this);
+ mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
mNavigationBarView.setOnVerticalChangedListener(
new NavigationBarView.OnVerticalChangedListener() {
@Override
@@ -928,7 +928,7 @@
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mHeader.setQSPanel(mQSPanel);
- qsh.setCallback(new QSTileHost.Callback() {
+ qsh.addCallback(new QSTileHost.Callback() {
@Override
public void onTilesChanged() {
mQSPanel.setTiles(qsh.getTiles());
@@ -1102,10 +1102,8 @@
}
@Override
- protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
- Notification.Action action, RemoteInput remoteInput) {
- return RemoteInputView.inflate(mContext, root, entry, action, remoteInput,
- mRemoteInputController);
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry) {
+ return RemoteInputView.inflate(mContext, root, entry, mRemoteInputController);
}
public int getStatusBarHeight() {
@@ -1136,7 +1134,7 @@
@Override
public boolean onLongClick(View v) {
if (mRecents != null) {
- mRecents.dockTopTask();
+ mRecents.dockTopTask(false /* draggingInRecents */, null /* initialBounds */);
return true;
}
return false;
@@ -1262,6 +1260,7 @@
Entry oldEntry) {
if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
+ mNotificationData.updateRanking(ranking);
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index fa9c4bb..83edc96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -383,6 +383,12 @@
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
mUserInfoController.reloadUserInfo();
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 96b919e..57c2648 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -17,18 +17,56 @@
package com.android.systemui.statusbar.phone;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.os.Binder;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
+import android.os.RemoteException;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.Tile;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.tiles.*;
-import com.android.systemui.statusbar.policy.*;
+import com.android.systemui.qs.tiles.AirplaneModeTile;
+import com.android.systemui.qs.tiles.BatteryTile;
+import com.android.systemui.qs.tiles.BluetoothTile;
+import com.android.systemui.qs.tiles.CastTile;
+import com.android.systemui.qs.tiles.CellularTile;
+import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.qs.tiles.FlashlightTile;
+import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.IntentTile;
+import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.QAirplaneTile;
+import com.android.systemui.qs.tiles.QBluetoothTile;
+import com.android.systemui.qs.tiles.QFlashlightTile;
+import com.android.systemui.qs.tiles.QLockTile;
+import com.android.systemui.qs.tiles.QRotationLockTile;
+import com.android.systemui.qs.tiles.QWifiTile;
+import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.qs.tiles.UserTile;
+import com.android.systemui.qs.tiles.WifiTile;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -40,7 +78,7 @@
import java.util.Map;
/** Platform implementation of the quick settings tile host **/
-public class QSTileHost implements QSTile.Host, Tunable {
+public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tunable {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -65,7 +103,7 @@
private final SecurityController mSecurity;
private final BatteryController mBattery;
- private Callback mCallback;
+ private final List<Callback> mCallbacks = new ArrayList<>();
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
@@ -107,8 +145,8 @@
}
@Override
- public void setCallback(Callback callback) {
- mCallback = callback;
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
}
@Override
@@ -209,14 +247,14 @@
public SecurityController getSecurityController() {
return mSecurity;
}
-
+
@Override
public void onTuningChanged(String key, String newValue) {
if (!TILES_SETTING.equals(key)) {
return;
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
- final List<String> tileSpecs = loadTileSpecs(newValue);
+ final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
if (tileSpecs.equals(mTileSpecs)) return;
for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
if (!tileSpecs.contains(tile.getKey())) {
@@ -227,11 +265,15 @@
final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
for (String tileSpec : tileSpecs) {
if (mTiles.containsKey(tileSpec)) {
- newTiles.put(tileSpec, mTiles.get(tileSpec));
+ QSTile<?> tile = mTiles.get(tileSpec);
+ if (DEBUG) Log.d(TAG, "Adding " + tile);
+ newTiles.put(tileSpec, tile);
} else {
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
try {
- newTiles.put(tileSpec, createTile(tileSpec));
+ QSTile<?> tile = createTile(tileSpec);
+ tile.setTileSpec(tileSpec);
+ newTiles.put(tileSpec, tile);
} catch (Throwable t) {
Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
}
@@ -241,11 +283,46 @@
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
mTiles.putAll(newTiles);
- if (mCallback != null) {
- mCallback.onTilesChanged();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onTilesChanged();
}
}
+ @Override
+ public void updateQsTile(Tile tile) throws RemoteException {
+ verifyCaller(tile.getComponentName().getPackageName());
+ CustomTile customTile = getTileForComponent(tile.getComponentName());
+ if (customTile != null) {
+ Log.d("TileService", "Got tile update for " + tile.getComponentName());
+ customTile.updateState(tile);
+ customTile.refreshState();
+ }
+ }
+
+ private void verifyCaller(String packageName) {
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(packageName,
+ Binder.getCallingUserHandle().getIdentifier());
+ if (Binder.getCallingUid() != uid) {
+ throw new SecurityException("Component outside caller's uid");
+ }
+ } catch (NameNotFoundException e) {
+ throw new SecurityException(e);
+ }
+ }
+
+ private CustomTile getTileForComponent(ComponentName component) {
+ // TODO: Build map for easier lookup.
+ for (QSTile<?> qsTile : mTiles.values()) {
+ if (qsTile instanceof CustomTile) {
+ if (((CustomTile) qsTile).getComponent().equals(component)) {
+ return (CustomTile) qsTile;
+ }
+ }
+ }
+ return null;
+ }
+
public QSTile<?> createTile(String tileSpec) {
if (tileSpec.equals("wifi")) return new WifiTile(this, false);
else if (tileSpec.equals("bt")) return new BluetoothTile(this, false);
@@ -276,8 +353,8 @@
else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
}
- protected List<String> loadTileSpecs(String tileList) {
- final Resources res = mContext.getResources();
+ public static List<String> loadTileSpecs(Context context, String tileList) {
+ final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
if (tileList == null) {
tileList = res.getString(R.string.quick_settings_tiles);
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 662dbd9..cc9f5c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -192,7 +192,7 @@
host.getBatteryController());
mHeaderQsPanel.setHost(myHost);
mHeaderQsPanel.setTiles(myHost.getTiles());
- myHost.setCallback(new QSTile.Host.Callback() {
+ myHost.addCallback(new QSTile.Host.Callback() {
@Override
public void onTilesChanged() {
mHeaderQsPanel.setTiles(myHost.getTiles());
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 18db5b8..512af1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -19,16 +19,13 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.graphics.drawable.RippleDrawable;
-import android.os.Handler;
-import android.os.Message;
import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-
import com.android.keyguard.AlphaOptimizedImageButton;
public class SettingsButton extends AlphaOptimizedImageButton {
@@ -157,6 +154,7 @@
protected void startContinuousSpin() {
cancelAnimation();
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mUpToSpeed = true;
mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360);
mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
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 dc9f5e8..5cfd174 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -458,7 +458,10 @@
mReleaseOnExpandFinish = false;
} else {
for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- removeHeadsUpEntry(entry);
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
}
}
mEntriesToRemoveAfterExpand.clear();
@@ -596,6 +599,9 @@
postTime = Math.max(postTime, currentTime);
}
removeAutoRemovalCallbacks();
+ if (mEntriesToRemoveAfterExpand.contains(entry)) {
+ mEntriesToRemoveAfterExpand.remove(entry);
+ }
if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 4d268ce..ba284c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,6 +52,7 @@
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
+ private boolean mLongClicked;
private final Runnable mCheckLongPress = new Runnable() {
public void run() {
@@ -60,9 +61,11 @@
if (isLongClickable()) {
// Just an old-fashioned ImageView
performLongClick();
+ mLongClicked = true;
} else if (mSupportsLongpress) {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ mLongClicked = true;
}
}
}
@@ -155,6 +158,7 @@
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownTime = SystemClock.uptimeMillis();
+ mLongClicked = false;
setPressed(true);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
@@ -181,7 +185,7 @@
removeCallbacks(mCheckLongPress);
break;
case MotionEvent.ACTION_UP:
- final boolean doIt = isPressed();
+ final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
if (mCode != 0) {
if (doIt) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 38656ee..f8c72b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -19,7 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
-
+import com.android.settingslib.net.MobileDataController;
import com.android.settingslib.wifi.AccessPoint;
import java.util.List;
@@ -85,22 +85,4 @@
void onSettingsActivityTriggered(Intent settingsIntent);
}
}
-
- /**
- * Tracks mobile data support and usage.
- */
- public interface MobileDataController {
- boolean isMobileDataSupported();
- boolean isMobileDataEnabled();
- void setMobileDataEnabled(boolean enabled);
- DataUsageInfo getDataUsageInfo();
-
- public static class DataUsageInfo {
- public String carrier;
- public String period;
- public long limitLevel;
- public long warningLevel;
- public long usageLevel;
- }
- }
}
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 2996808..909f497 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -39,10 +37,10 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.net.MobileDataController;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
@@ -57,9 +55,11 @@
import java.util.Locale;
import java.util.Map;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
/** Platform implementation of the network controller. **/
public class NetworkControllerImpl extends BroadcastReceiver
- implements NetworkController, DemoMode {
+ implements NetworkController, DemoMode, MobileDataController.NetworkNameProvider {
// debug
static final String TAG = "NetworkController";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -94,7 +94,7 @@
// SIM for most actions. This may be null if there aren't any SIMs around.
private MobileSignalController mDefaultSignalController;
private final AccessPointControllerImpl mAccessPoints;
- private final MobileDataControllerImpl mMobileDataController;
+ private final MobileDataController mMobileDataController;
private boolean mInetCondition; // Used for Logging and demo.
@@ -139,7 +139,7 @@
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
new AccessPointControllerImpl(context, bgLooper),
- new MobileDataControllerImpl(context),
+ new MobileDataController(context),
new SubscriptionDefaults());
mReceiverHandler.post(mRegisterListeners);
}
@@ -150,7 +150,7 @@
SubscriptionManager subManager, Config config, Looper bgLooper,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
- MobileDataControllerImpl mobileDataController,
+ MobileDataController mobileDataController,
SubscriptionDefaults defaultsHandler) {
mContext = context;
mConfig = config;
@@ -174,7 +174,7 @@
mMobileDataController = mobileDataController;
mMobileDataController.setNetworkController(this);
// TODO: Find a way to move this into MobileDataController.
- mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() {
+ mMobileDataController.setCallback(new MobileDataController.Callback() {
@Override
public void onMobileDataEnabled(boolean enabled) {
mCallbackHandler.setMobileDataEnabled(enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 2ad9287..acfe54d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -34,11 +34,13 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -47,16 +49,21 @@
/**
* Host for the remote input.
*/
-public class RemoteInputView extends FrameLayout implements View.OnClickListener {
+public class RemoteInputView extends LinearLayout implements View.OnClickListener {
private static final String TAG = "RemoteInput";
+ // A marker object that let's us easily find views of this class.
+ public static final Object VIEW_TAG = new Object();
+
private RemoteEditText mEditText;
+ private ImageButton mSendButton;
private ProgressBar mProgressBar;
private PendingIntent mPendingIntent;
+ private RemoteInput[] mRemoteInputs;
private RemoteInput mRemoteInput;
- private Notification.Action mAction;
private RemoteInputController mController;
+
private NotificationData.Entry mEntry;
public RemoteInputView(Context context, AttributeSet attrs) {
@@ -69,6 +76,9 @@
mProgressBar = (ProgressBar) findViewById(R.id.remote_input_progress);
+ mSendButton = (ImageButton) findViewById(R.id.remote_input_send);
+ mSendButton.setOnClickListener(this);
+
mEditText = (RemoteEditText) getChildAt(0);
mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
@@ -99,10 +109,11 @@
Bundle results = new Bundle();
results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(mAction.getRemoteInputs(), fillInIntent,
+ RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
results);
mEditText.setEnabled(false);
+ mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
try {
@@ -113,17 +124,13 @@
}
public static RemoteInputView inflate(Context context, ViewGroup root,
- NotificationData.Entry entry, Notification.Action action, RemoteInput remoteInput,
+ NotificationData.Entry entry,
RemoteInputController controller) {
RemoteInputView v = (RemoteInputView)
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
-
- v.mEditText.setHint(action.title);
- v.mPendingIntent = action.actionIntent;
- v.mRemoteInput = remoteInput;
- v.mAction = action;
v.mController = controller;
v.mEntry = entry;
+ v.setTag(VIEW_TAG);
return v;
}
@@ -132,15 +139,16 @@
public void onClick(View v) {
if (v == mEditText) {
if (!mEditText.isFocusable()) {
- mEditText.setInnerFocusable(true);
- mController.addRemoteInput(mEntry);
- mEditText.mShowImeOnInputConnection = true;
+ focus();
}
+ } else if (v == mSendButton) {
+ sendRemoteInput();
}
}
public void onDefocus() {
mController.removeRemoteInput(mEntry);
+ setVisibility(INVISIBLE);
}
@Override
@@ -149,6 +157,23 @@
mController.removeRemoteInput(mEntry);
}
+ public void setPendingIntent(PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ }
+
+ public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
+ mRemoteInputs = remoteInputs;
+ mRemoteInput = remoteInput;
+ mEditText.setHint(mRemoteInput.getLabel());
+ }
+
+ public void focus() {
+ mEditText.setInnerFocusable(true);
+ mController.addRemoteInput(mEntry);
+ mEditText.mShowImeOnInputConnection = true;
+ mEditText.requestFocus();
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -220,6 +245,13 @@
return inputConnection;
}
+ @Override
+ public void onCommitCompletion(CompletionInfo text) {
+ clearComposingText();
+ setText(text.getText());
+ setSelection(getText().length());
+ }
+
void setInnerFocusable(boolean focusable) {
setFocusableInTouchMode(focusable);
setFocusable(focusable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index f06e5d3..a22f988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.policy;
public interface SecurityController {
-
- boolean hasDeviceOwner();
+ /** Whether the device has device owner, even if not on this user. */
+ boolean isDeviceManaged();
boolean hasProfileOwner();
String getDeviceOwnerName();
String getProfileOwnerName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 88f028f..6ddd7a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -102,13 +102,13 @@
}
@Override
- public boolean hasDeviceOwner() {
- return !TextUtils.isEmpty(mDevicePolicyManager.getDeviceOwner());
+ public boolean isDeviceManaged() {
+ return mDevicePolicyManager.isDeviceManaged();
}
@Override
public String getDeviceOwnerName() {
- return mDevicePolicyManager.getDeviceOwnerName();
+ return mDevicePolicyManager.getDeviceOwnerNameOnAnyUser();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 9b1e72a..eab6e13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -18,20 +18,16 @@
import android.content.Context;
import android.content.Intent;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
+import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import java.util.List;
import java.util.Objects;
@@ -40,12 +36,14 @@
private final WifiManager mWifiManager;
private final AsyncChannel mWifiChannel;
private final boolean mHasMobileData;
+ private final WifiStatusTracker mWifiTracker;
public WifiSignalController(Context context, boolean hasMobileData,
CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mWifiTracker = new WifiStatusTracker(mWifiManager);
mHasMobileData = hasMobileData;
Handler handler = new WifiHandler();
mWifiChannel = new AsyncChannel();
@@ -93,54 +91,15 @@
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
public void handleBroadcast(Intent intent) {
- String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- final NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- mCurrentState.connected = networkInfo != null && networkInfo.isConnected();
- // If Connected grab the signal strength and ssid.
- if (mCurrentState.connected) {
- // try getting it out of the intent first
- WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
- ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
- : mWifiManager.getConnectionInfo();
- if (info != null) {
- mCurrentState.ssid = getSsid(info);
- } else {
- mCurrentState.ssid = null;
- }
- } else if (!mCurrentState.connected) {
- mCurrentState.ssid = null;
- }
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- // Default to -200 as its below WifiManager.MIN_RSSI.
- mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- mCurrentState.level = WifiManager.calculateSignalLevel(
- mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
- }
-
+ mWifiTracker.handleBroadcast(intent);
+ mCurrentState.enabled = mWifiTracker.enabled;
+ mCurrentState.connected = mWifiTracker.connected;
+ mCurrentState.ssid = mWifiTracker.ssid;
+ mCurrentState.rssi = mWifiTracker.rssi;
+ mCurrentState.level = mWifiTracker.level;
notifyListenersIfNecessary();
}
- private String getSsid(WifiInfo info) {
- String ssid = info.getSSID();
- if (ssid != null) {
- return ssid;
- }
- // OK, it's not in the connectionInfo; we have to go hunting for it
- List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
- int length = networks.size();
- for (int i = 0; i < length; i++) {
- if (networks.get(i).networkId == info.getNetworkId()) {
- return networks.get(i).SSID;
- }
- }
- return null;
- }
-
@VisibleForTesting
void setActivity(int wifiActivity) {
mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
deleted file mode 100644
index 05e3fd5..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ /dev/null
@@ -1,547 +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.tuner;
-
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Fragment;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnDragListener;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.ScrollView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTile.Host.Callback;
-import com.android.systemui.qs.QSTile.ResourceIcon;
-import com.android.systemui.qs.QSTileBaseView;
-import com.android.systemui.qs.QSTileView;
-import com.android.systemui.qs.tiles.IntentTile;
-import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.statusbar.policy.SecurityController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class QsTuner extends Fragment implements Callback {
-
- private static final String TAG = "QsTuner";
-
- private static final int MENU_RESET = Menu.FIRST;
-
- private DraggableQsPanel mQsPanel;
- private CustomHost mTileHost;
-
- private FrameLayout mDropTarget;
-
- private ScrollView mScrollRoot;
-
- private FrameLayout mAddTarget;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- menu.add(0, MENU_RESET, 0, com.android.internal.R.string.reset);
- }
-
- public void onResume() {
- super.onResume();
- MetricsLogger.visibility(getContext(), MetricsLogger.TUNER_QS, true);
- }
-
- public void onPause() {
- super.onPause();
- MetricsLogger.visibility(getContext(), MetricsLogger.TUNER_QS, false);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_RESET:
- mTileHost.reset();
- break;
- case android.R.id.home:
- getFragmentManager().popBackStack();
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mScrollRoot = (ScrollView) inflater.inflate(R.layout.tuner_qs, container, false);
-
- mQsPanel = new DraggableQsPanel(getContext());
- mTileHost = new CustomHost(getContext());
- mTileHost.setCallback(this);
- mQsPanel.setTiles(mTileHost.getTiles());
- mQsPanel.setHost(mTileHost);
- mQsPanel.refreshAllTiles();
- ((ViewGroup) mScrollRoot.findViewById(R.id.all_details)).addView(mQsPanel, 0);
-
- mDropTarget = (FrameLayout) mScrollRoot.findViewById(R.id.remove_target);
- setupDropTarget();
- mAddTarget = (FrameLayout) mScrollRoot.findViewById(R.id.add_target);
- setupAddTarget();
- return mScrollRoot;
- }
-
- @Override
- public void onDestroyView() {
- mTileHost.destroy();
- super.onDestroyView();
- }
-
- private void setupDropTarget() {
- QSTileView tileView = new QSTileView(getContext());
- QSTile.State state = new QSTile.State();
- state.visible = true;
- state.icon = ResourceIcon.get(R.drawable.ic_delete);
- state.label = getString(com.android.internal.R.string.delete);
- tileView.onStateChanged(state);
- mDropTarget.addView(tileView);
- mDropTarget.setVisibility(View.GONE);
- new DragHelper(tileView, new DropListener() {
- @Override
- public void onDrop(String sourceText) {
- mTileHost.remove(sourceText);
- }
- });
- }
-
- private void setupAddTarget() {
- QSTileView tileView = new QSTileView(getContext());
- QSTile.State state = new QSTile.State();
- state.visible = true;
- state.icon = ResourceIcon.get(R.drawable.ic_add_circle_qs);
- state.label = getString(R.string.add_tile);
- tileView.onStateChanged(state);
- mAddTarget.addView(tileView);
- tileView.setClickable(true);
- tileView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mTileHost.showAddDialog();
- }
- });
- }
-
- public void onStartDrag() {
- mDropTarget.post(new Runnable() {
- @Override
- public void run() {
- mDropTarget.setVisibility(View.VISIBLE);
- mAddTarget.setVisibility(View.GONE);
- }
- });
- }
-
- public void stopDrag() {
- mDropTarget.post(new Runnable() {
- @Override
- public void run() {
- mDropTarget.setVisibility(View.GONE);
- mAddTarget.setVisibility(View.VISIBLE);
- }
- });
- }
-
- @Override
- public void onTilesChanged() {
- mQsPanel.setTiles(mTileHost.getTiles());
- }
-
- private static int getLabelResource(String spec) {
- if (spec.equals("wifi")) return R.string.quick_settings_wifi_label;
- else if (spec.equals("bt")) return R.string.quick_settings_bluetooth_label;
- else if (spec.equals("inversion")) return R.string.quick_settings_inversion_label;
- else if (spec.equals("cell")) return R.string.quick_settings_cellular_detail_title;
- else if (spec.equals("airplane")) return R.string.airplane_mode;
- else if (spec.equals("dnd")) return R.string.quick_settings_dnd_label;
- else if (spec.equals("rotation")) return R.string.quick_settings_rotation_locked_label;
- else if (spec.equals("flashlight")) return R.string.quick_settings_flashlight_label;
- else if (spec.equals("location")) return R.string.quick_settings_location_label;
- else if (spec.equals("cast")) return R.string.quick_settings_cast_title;
- else if (spec.equals("hotspot")) return R.string.quick_settings_hotspot_label;
- return 0;
- }
-
- private static class CustomHost extends QSTileHost {
-
- public CustomHost(Context context) {
- super(context, null, null, null, null, null, null, null, null, null, null,
- null, null, new BlankSecurityController(), null);
- }
-
- @Override
- public QSTile<?> createTile(String tileSpec) {
- return new DraggableTile(this, tileSpec);
- }
-
- public void replace(String oldTile, String newTile) {
- if (oldTile.equals(newTile)) {
- return;
- }
- MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REORDER, oldTile + ","
- + newTile);
- List<String> order = new ArrayList<>(mTileSpecs);
- int index = order.indexOf(oldTile);
- if (index < 0) {
- Log.e(TAG, "Can't find " + oldTile);
- return;
- }
- order.remove(newTile);
- order.add(index, newTile);
- setTiles(order);
- }
-
- public void remove(String tile) {
- MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_REMOVE, tile);
- List<String> tiles = new ArrayList<>(mTileSpecs);
- tiles.remove(tile);
- setTiles(tiles);
- }
-
- public void add(String tile) {
- MetricsLogger.action(getContext(), MetricsLogger.TUNER_QS_ADD, tile);
- List<String> tiles = new ArrayList<>(mTileSpecs);
- tiles.add(tile);
- setTiles(tiles);
- }
-
- public void reset() {
- Secure.putStringForUser(getContext().getContentResolver(),
- TILES_SETTING, "default", ActivityManager.getCurrentUser());
- }
-
- private void setTiles(List<String> tiles) {
- Secure.putStringForUser(getContext().getContentResolver(), TILES_SETTING,
- TextUtils.join(",", tiles), ActivityManager.getCurrentUser());
- }
-
- public void showAddDialog() {
- List<String> tiles = mTileSpecs;
- int numBroadcast = 0;
- for (int i = 0; i < tiles.size(); i++) {
- if (tiles.get(i).startsWith(IntentTile.PREFIX)) {
- numBroadcast++;
- }
- }
- String[] defaults =
- getContext().getString(R.string.quick_settings_tiles_default).split(",");
- final String[] available = new String[defaults.length + 1
- - (tiles.size() - numBroadcast)];
- final String[] availableTiles = new String[available.length];
- int index = 0;
- for (int i = 0; i < defaults.length; i++) {
- if (tiles.contains(defaults[i])) {
- continue;
- }
- int resource = getLabelResource(defaults[i]);
- if (resource != 0) {
- availableTiles[index] = defaults[i];
- available[index++] = getContext().getString(resource);
- } else {
- availableTiles[index] = defaults[i];
- available[index++] = defaults[i];
- }
- }
- available[index++] = getContext().getString(R.string.broadcast_tile);
- new AlertDialog.Builder(getContext())
- .setTitle(R.string.add_tile)
- .setItems(available, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (which < available.length - 1) {
- add(availableTiles[which]);
- } else {
- showBroadcastTileDialog();
- }
- }
- }).show();
- }
-
- public void showBroadcastTileDialog() {
- final EditText editText = new EditText(getContext());
- new AlertDialog.Builder(getContext())
- .setTitle(R.string.broadcast_tile)
- .setView(editText)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- String action = editText.getText().toString();
- if (isValid(action)) {
- add(IntentTile.PREFIX + action + ')');
- }
- }
- }).show();
- }
-
- private boolean isValid(String action) {
- for (int i = 0; i < action.length(); i++) {
- char c = action.charAt(i);
- if (!Character.isAlphabetic(c) && !Character.isDigit(c) && c != '.') {
- return false;
- }
- }
- return true;
- }
-
- private static class BlankSecurityController implements SecurityController {
- @Override
- public boolean hasDeviceOwner() {
- return false;
- }
-
- @Override
- public boolean hasProfileOwner() {
- return false;
- }
-
- @Override
- public String getDeviceOwnerName() {
- return null;
- }
-
- @Override
- public String getProfileOwnerName() {
- return null;
- }
-
- @Override
- public boolean isVpnEnabled() {
- return false;
- }
-
- @Override
- public boolean isVpnRestricted() {
- return false;
- }
-
- @Override
- public String getPrimaryVpnName() {
- return null;
- }
-
- @Override
- public String getProfileVpnName() {
- return null;
- }
-
- @Override
- public void onUserSwitched(int newUserId) {
- }
-
- @Override
- public void addCallback(SecurityControllerCallback callback) {
- }
-
- @Override
- public void removeCallback(SecurityControllerCallback callback) {
- }
- }
- }
-
- private static class DraggableTile extends QSTile<QSTile.State>
- implements DropListener {
- private String mSpec;
- private QSTileBaseView mView;
-
- protected DraggableTile(QSTile.Host host, String tileSpec) {
- super(host);
- Log.d(TAG, "Creating tile " + tileSpec);
- mSpec = tileSpec;
- }
-
- @Override
- public QSTileBaseView createTileView(Context context) {
- mView = super.createTileView(context);
- return mView;
- }
-
- @Override
- public int getTileType() {
- return "wifi".equals(mSpec) || "bt".equals(mSpec) ? QSTileView.QS_TYPE_DUAL
- : QSTileView.QS_TYPE_NORMAL;
- }
-
- @Override
- public void setListening(boolean listening) {
- }
-
- @Override
- protected QSTile.State newTileState() {
- return new QSTile.State();
- }
-
- @Override
- protected void handleClick() {
- }
-
- @Override
- protected void handleUpdateState(QSTile.State state, Object arg) {
- state.visible = true;
- state.icon = ResourceIcon.get(getIcon());
- state.label = getLabel();
- }
-
- private String getLabel() {
- int resource = getLabelResource(mSpec);
- if (resource != 0) {
- return mContext.getString(resource);
- }
- if (mSpec.startsWith(IntentTile.PREFIX)) {
- int lastDot = mSpec.lastIndexOf('.');
- if (lastDot >= 0) {
- return mSpec.substring(lastDot + 1, mSpec.length() - 1);
- } else {
- return mSpec.substring(IntentTile.PREFIX.length(), mSpec.length() - 1);
- }
- }
- return mSpec;
- }
-
- private int getIcon() {
- if (mSpec.equals("wifi")) return R.drawable.ic_qs_wifi_full_3;
- else if (mSpec.equals("bt")) return R.drawable.ic_qs_bluetooth_connected;
- else if (mSpec.equals("inversion")) return R.drawable.ic_invert_colors_enable;
- else if (mSpec.equals("cell")) return R.drawable.ic_qs_signal_full_3;
- else if (mSpec.equals("airplane")) return R.drawable.ic_signal_airplane_enable;
- else if (mSpec.equals("dnd")) return R.drawable.ic_qs_dnd_on;
- else if (mSpec.equals("rotation")) return R.drawable.ic_portrait_from_auto_rotate;
- else if (mSpec.equals("flashlight")) return R.drawable.ic_signal_flashlight_enable;
- else if (mSpec.equals("location")) return R.drawable.ic_signal_location_enable;
- else if (mSpec.equals("cast")) return R.drawable.ic_qs_cast_on;
- else if (mSpec.equals("hotspot")) return R.drawable.ic_hotspot_enable;
- return R.drawable.android;
- }
-
- @Override
- public int getMetricsCategory() {
- return 20000;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof DraggableTile) {
- return mSpec.equals(((DraggableTile) o).mSpec);
- }
- return false;
- }
-
- @Override
- public void onDrop(String sourceText) {
- ((CustomHost) mHost).replace(mSpec, sourceText);
- }
-
- }
-
- private class DragHelper implements OnDragListener {
-
- private final View mView;
- private final DropListener mListener;
-
- public DragHelper(View view, DropListener dropListener) {
- mView = view;
- mListener = dropListener;
- mView.setOnDragListener(this);
- }
-
- @Override
- public boolean onDrag(View v, DragEvent event) {
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_ENTERED:
- mView.setBackgroundColor(0x77ffffff);
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- stopDrag();
- case DragEvent.ACTION_DRAG_EXITED:
- mView.setBackgroundColor(0x0);
- break;
- case DragEvent.ACTION_DROP:
- stopDrag();
- String text = event.getClipData().getItemAt(0).getText().toString();
- mListener.onDrop(text);
- break;
- }
- return true;
- }
-
- }
-
- public interface DropListener {
- void onDrop(String sourceText);
- }
-
- private class DraggableQsPanel extends QSPanel implements OnTouchListener {
- public DraggableQsPanel(Context context) {
- super(context);
- mBrightnessView.setVisibility(View.GONE);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- for (TileRecord r : mRecords) {
- new DragHelper(r.tileView, (DraggableTile) r.tile);
- r.tileView.setTag(r.tile);
- r.tileView.setOnTouchListener(this);
-
- for (int i = 0; i < r.tileView.getChildCount(); i++) {
- r.tileView.getChildAt(i).setClickable(false);
- }
- }
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- String tileSpec = (String) ((DraggableTile) v.getTag()).mSpec;
- ClipData data = ClipData.newPlainText(tileSpec, tileSpec);
- v.startDrag(data, new View.DragShadowBuilder(v), null, 0);
- onStartDrag();
- return true;
- }
- return false;
- }
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index dc7c967..b620b50b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -59,8 +59,6 @@
private SwitchPreference mBatteryPct;
- private Preference mQsTuner;
-
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -68,17 +66,6 @@
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
setHasOptionsMenu(true);
- mQsTuner = findPreference(KEY_QS_TUNER);
- mQsTuner.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.replace(android.R.id.content, new QsTuner(), "QsTuner");
- ft.addToBackStack(null);
- ft.commit();
- return true;
- }
- });
findPreference(KEY_DEMO_MODE).setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
@@ -96,13 +83,6 @@
new TunerWarningFragment().show(getFragmentManager(), WARNING_TAG);
}
}
- TunerService.get(getContext()).addTunable(mQsPaging, QSPanel.QS_THE_NEW_QS);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- TunerService.get(getContext()).removeTunable(mQsPaging);
}
@Override
@@ -175,14 +155,6 @@
}
};
- private final Tunable mQsPaging = new Tunable() {
- @Override
- public void onTuningChanged(String key, String newValue) {
- // Only enable QS rearranging when paging is off, because its very broken.
- mQsTuner.setEnabled(newValue == null || Integer.parseInt(newValue) == 0);
- }
- };
-
public static class TunerWarningFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 30c08cd..13fc47d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -33,6 +33,7 @@
import android.util.Log;
import com.android.internal.telephony.cdma.EriInfo;
+import com.android.settingslib.net.MobileDataController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
@@ -95,7 +96,7 @@
mCallbackHandler = mock(CallbackHandler.class);
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class),
+ mock(AccessPointControllerImpl.class), mock(MobileDataController.class),
mMockSubDefaults);
setupNetworkController();
@@ -136,7 +137,7 @@
= new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class),
- mock(MobileDataControllerImpl.class), mMockSubDefaults);
+ mock(MobileDataController.class), mMockSubDefaults);
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 0ec8802..587e2b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -4,6 +4,7 @@
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.settingslib.net.MobileDataController;
import org.mockito.Mockito;
@SmallTest
@@ -87,7 +88,7 @@
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
Mockito.mock(AccessPointControllerImpl.class),
- Mockito.mock(MobileDataControllerImpl.class), mMockSubDefaults);
+ Mockito.mock(MobileDataController.class), mMockSubDefaults);
setupNetworkController();
setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 660fd9c..760aa9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -29,6 +29,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.net.MobileDataController;
import com.android.systemui.R;
import org.mockito.ArgumentCaptor;
@@ -46,7 +47,7 @@
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class),
+ mock(AccessPointControllerImpl.class), mock(MobileDataController.class),
mMockSubDefaults);
setupNetworkController();
@@ -95,7 +96,7 @@
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class),
+ mock(AccessPointControllerImpl.class), mock(MobileDataController.class),
mMockSubDefaults);
setupNetworkController();
diff --git a/packages/services/Proxy/AndroidManifest.xml b/packages/services/Proxy/AndroidManifest.xml
index bbcd6b9..88f8381 100644
--- a/packages/services/Proxy/AndroidManifest.xml
+++ b/packages/services/Proxy/AndroidManifest.xml
@@ -7,7 +7,9 @@
<application
android:label="@string/app_label"
- android:process="com.android.proxyhandler">
+ android:process="com.android.proxyhandler"
+ android:forceDeviceEncrypted="true"
+ android:encryptionAware="true">
<service android:name=".ProxyService"
android:exported="true">
diff --git a/preloaded-classes b/preloaded-classes
index d6b4ec9..79c0957 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1566,6 +1566,7 @@
android.security.keystore.AndroidKeyStoreKey
android.security.keystore.AndroidKeyStoreProvider
android.security.keystore.KeyStoreCryptoOperation
+android.security.net.config.NetworkSecurityConfigProvider
android.service.persistentdata.PersistentDataBlockManager
android.system.ErrnoException
android.system.GaiException
@@ -3559,11 +3560,6 @@
org.apache.harmony.security.asn1.BerOutputStream
org.apache.harmony.security.asn1.DerInputStream
org.apache.harmony.security.asn1.DerOutputStream
-org.apache.harmony.security.fortress.Engine
-org.apache.harmony.security.fortress.Engine$ServiceCacheEntry
-org.apache.harmony.security.fortress.Engine$SpiAndProvider
-org.apache.harmony.security.fortress.SecurityAccess
-org.apache.harmony.security.fortress.Services
org.apache.harmony.security.provider.crypto.CryptoProvider
org.apache.harmony.security.utils.AlgNameMapper
org.apache.harmony.security.utils.AlgNameMapperSource
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 54180f4..9bbacbc 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -278,6 +278,8 @@
public ValueAndSize(RenderScript rs, Object obj) {
if (obj instanceof Allocation) {
value = ((Allocation)obj).getID(rs);
+ // Special value for size to tell the runtime and driver that
+ // the value is an Allocation
size = -1;
} else if (obj instanceof Boolean) {
value = ((Boolean)obj).booleanValue() ? 1 : 0;
@@ -289,10 +291,10 @@
value = ((Long)obj).longValue();
size = 8;
} else if (obj instanceof Float) {
- value = ((Float)obj).longValue();
+ value = Float.floatToRawIntBits(((Float)obj).floatValue());
size = 4;
} else if (obj instanceof Double) {
- value = ((Double)obj).longValue();
+ value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
size = 8;
}
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index be7071e..113241d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -393,7 +393,6 @@
size_t numValues, numDependencies;
RsScriptFieldID* fieldIDs;
- uintptr_t* values;
RsClosure* depClosures;
RsScriptFieldID* depFieldIDs;
@@ -430,15 +429,6 @@
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
- if (values == nullptr) {
- goto exit;
- }
-
- for (size_t i = 0; i < numValues; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
if (depClosures == nullptr) {
goto exit;
@@ -459,7 +449,7 @@
ret = (jlong)(uintptr_t)rsClosureCreate(
(RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
- fieldIDs, numValues, values, numValues,
+ fieldIDs, numValues, jValues, numValues,
(int*)jSizes, numValues,
depClosures, numDependencies,
depFieldIDs, numDependencies);
@@ -511,7 +501,6 @@
size_t numValues;
RsScriptFieldID* fieldIDs;
- uintptr_t* values;
if (fieldIDs_length != values_length || values_length != sizes_length) {
ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
@@ -534,18 +523,9 @@
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
- if (values == nullptr) {
- goto exit;
- }
-
- for (size_t i = 0; i < numValues; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
ret = (jlong)(uintptr_t)rsInvokeClosureCreate(
(RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
- fieldIDs, numValues, values, numValues,
+ fieldIDs, numValues, jValues, numValues,
(int*)jSizes, numValues);
exit:
@@ -561,15 +541,17 @@
static void
nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
jint index, jlong value, jint size) {
+ // Size is signed with -1 indicating the value is an Allocation
rsClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
- (uintptr_t)value, (size_t)size);
+ (uintptr_t)value, size);
}
static void
nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
jlong fieldID, jlong value, jint size) {
+ // Size is signed with -1 indicating the value is an Allocation
rsClosureSetGlobal((RsContext)con, (RsClosure)closureID,
- (RsScriptFieldID)fieldID, (uintptr_t)value, (size_t)size);
+ (RsScriptFieldID)fieldID, (int64_t)value, size);
}
static long
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index b52687a..5f6cbf9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -21,7 +21,6 @@
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.view.Choreographer;
-import android.view.Display;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputFilter;
@@ -103,7 +102,7 @@
private TouchExplorer mTouchExplorer;
- private ScreenMagnifier mScreenMagnifier;
+ private MagnificationGestureHandler mMagnificationGestureHandler;
private AutoclickController mAutoclickController;
@@ -363,14 +362,13 @@
}
if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
- mScreenMagnifier = new ScreenMagnifier(mContext, mUserId,
- Display.DEFAULT_DISPLAY, mAms);
- addFirstEventHandler(mScreenMagnifier);
+ mMagnificationGestureHandler = new MagnificationGestureHandler(mContext, mAms);
+ addFirstEventHandler(mMagnificationGestureHandler);
}
if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
- mKeyboardInterceptor = new KeyboardInterceptor(mAms);
- addFirstEventHandler(mKeyboardInterceptor);
+ mKeyboardInterceptor = new KeyboardInterceptor(mAms);
+ addFirstEventHandler(mKeyboardInterceptor);
}
}
@@ -398,9 +396,9 @@
mTouchExplorer.onDestroy();
mTouchExplorer = null;
}
- if (mScreenMagnifier != null) {
- mScreenMagnifier.onDestroy();
- mScreenMagnifier = null;
+ if (mMagnificationGestureHandler != null) {
+ mMagnificationGestureHandler.onDestroy();
+ mMagnificationGestureHandler = null;
}
if (mKeyboardInterceptor != null) {
mKeyboardInterceptor.onDestroy();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 535a8ef..3d358ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -23,6 +23,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -63,21 +64,17 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Pools.Pool;
-import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
import android.view.InputDevice;
-import android.view.InputEventConsistencyVerifier;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerInternal;
-import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -90,6 +87,7 @@
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.LocalServices;
@@ -144,8 +142,6 @@
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
- private static final int MAX_POOL_SIZE = 10;
-
private static final int WINDOW_ID_UNKNOWN = -1;
private static int sIdCounter = 0;
@@ -156,9 +152,6 @@
private final Object mLock = new Object();
- private final Pool<PendingEvent> mPendingEventPool =
- new SimplePool<>(MAX_POOL_SIZE);
-
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
@@ -181,6 +174,8 @@
private MagnificationController mMagnificationController;
+ private boolean mUnregisterMagnificationOnReset;
+
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
@@ -189,6 +184,8 @@
private boolean mHasInputFilter;
+ private KeyEventDispatcher mKeyEventDispatcher;
+
private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -752,12 +749,33 @@
boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
synchronized (mLock) {
- KeyEvent localClone = KeyEvent.obtain(event);
- boolean handled = notifyKeyEventLocked(localClone, policyFlags, false);
- if (!handled) {
- handled = notifyKeyEventLocked(localClone, policyFlags, true);
+ List<Service> boundServices = getCurrentUserStateLocked().mBoundServices;
+ if (boundServices.isEmpty()) {
+ return false;
}
- return handled;
+ return getKeyEventDispatcher().notifyKeyEventLocked(event, policyFlags, boundServices);
+ }
+ }
+
+ /**
+ * Called by the MagnificationController when the state of display
+ * magnification changes.
+ *
+ * @param region the new magnified region, may be empty if
+ * magnification is not enabled (e.g. scale is 1)
+ * @param scale the new scale
+ * @param centerX the new screen-relative center X coordinate
+ * @param centerY the new screen-relative center Y coordinate
+ */
+ void notifyMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ synchronized (mLock) {
+ notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+
+ if (mUnregisterMagnificationOnReset && scale == 1.0f) {
+ mUnregisterMagnificationOnReset = false;
+ mMagnificationController.unregister();
+ }
}
}
@@ -909,31 +927,6 @@
return false;
}
- private boolean notifyKeyEventLocked(KeyEvent event, int policyFlags, boolean isDefault) {
- // TODO: Now we are giving the key events to the last enabled
- // service that can handle them Ideally, the user should
- // make the call which service handles key events. However,
- // only one service should handle key events to avoid user
- // frustration when different behavior is observed from
- // different combinations of enabled accessibility services.
- UserState state = getCurrentUserStateLocked();
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- Service service = state.mBoundServices.get(i);
- // Key events are handled only by services that declared
- // this capability and requested to filter key events.
- if (!service.mRequestFilterKeyEvents ||
- (service.mAccessibilityServiceInfo.getCapabilities() & AccessibilityServiceInfo
- .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) {
- continue;
- }
- if (service.mIsDefault == isDefault) {
- service.notifyKeyEvent(event, policyFlags);
- return true;
- }
- }
- return false;
- }
-
private void notifyClearAccessibilityCacheLocked() {
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -942,6 +935,15 @@
}
}
+ private void notifyMagnificationChangedLocked(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ final UserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = state.mBoundServices.get(i);
+ service.notifyMagnificationChanged(region, scale, centerX, centerY);
+ }
+ }
+
/**
* Removes an AccessibilityInteractionConnection.
*
@@ -1363,6 +1365,11 @@
}
}
+ /**
+ * Called when any property of the user state has changed.
+ *
+ * @param userState the new user state
+ */
private void onUserStateChangedLocked(UserState userState) {
// TODO: Remove this hack
mInitialized = true;
@@ -1374,6 +1381,7 @@
updateTouchExplorationLocked(userState);
updateEnhancedWebAccessibilityLocked(userState);
updateDisplayColorAdjustmentSettingsLocked(userState);
+ updateMagnificationLocked(userState);
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
}
@@ -1663,6 +1671,44 @@
DisplayAdjustmentUtils.applyAdjustments(mContext, userState.mUserId);
}
+ private void updateMagnificationLocked(UserState userState) {
+ final int userId = userState.mUserId;
+ if (userId == mCurrentUserId && mMagnificationController != null) {
+ if (userHasMagnificationServicesLocked(userState)) {
+ mMagnificationController.setUserId(userState.mUserId);
+ } else {
+ // If the user no longer has any magnification-controlling
+ // services and is not using magnification gestures, then
+ // reset the state to normal.
+ if (!userState.mIsDisplayMagnificationEnabled
+ && mMagnificationController.resetIfNeeded(true)) {
+ // Animations are still running, so wait until we receive a
+ // callback verifying that we've reset magnification.
+ mUnregisterMagnificationOnReset = true;
+ } else {
+ mUnregisterMagnificationOnReset = false;
+ mMagnificationController.unregister();
+ mMagnificationController = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether the specified user has any services that are capable of
+ * controlling magnification.
+ */
+ private boolean userHasMagnificationServicesLocked(UserState userState) {
+ final List<Service> services = userState.mBoundServices;
+ for (int i = 0, count = services.size(); i < count; i++) {
+ final Service service = services.get(i);
+ if (mSecurityPolicy.canControlMagnification(service)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
IBinder windowToken = mGlobalWindowTokens.get(windowId);
if (windowToken == null) {
@@ -1675,6 +1721,14 @@
return null;
}
+ private KeyEventDispatcher getKeyEventDispatcher() {
+ if (mKeyEventDispatcher == null) {
+ mKeyEventDispatcher = new KeyEventDispatcher(
+ mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock);
+ }
+ return mKeyEventDispatcher;
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
@@ -1875,22 +1929,6 @@
}
}
- private PendingEvent obtainPendingEventLocked(KeyEvent event, int policyFlags, int sequence) {
- PendingEvent pendingEvent = mPendingEventPool.acquire();
- if (pendingEvent == null) {
- pendingEvent = new PendingEvent();
- }
- pendingEvent.event = event;
- pendingEvent.policyFlags = policyFlags;
- pendingEvent.sequence = sequence;
- return pendingEvent;
- }
-
- private void recyclePendingEventLocked(PendingEvent pendingEvent) {
- pendingEvent.clear();
- mPendingEventPool.release(pendingEvent);
- }
-
private int findWindowIdLocked(IBinder token) {
final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
if (globalIndex >= 0) {
@@ -1938,10 +1976,14 @@
}
MagnificationController getMagnificationController() {
- if (mMagnificationController == null) {
- mMagnificationController = new MagnificationController(mContext, this);
+ synchronized (mLock) {
+ if (mMagnificationController == null) {
+ mMagnificationController = new MagnificationController(mContext, this);
+ mMagnificationController.register();
+ mMagnificationController.setUserId(mCurrentUserId);
+ }
+ return mMagnificationController;
}
- return mMagnificationController;
}
/**
@@ -1999,8 +2041,6 @@
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<>();
- final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
-
boolean mWasConnectedAndDied;
// Handler only for dispatching accessibility events since we use event
@@ -2112,7 +2152,7 @@
return false;
}
UserState userState = getUserStateLocked(mUserId);
- mKeyEventDispatcher.flush();
+ getKeyEventDispatcher().flush(this);
if (!mIsAutomation) {
mContext.unbindService(this);
} else {
@@ -2129,7 +2169,7 @@
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
- mKeyEventDispatcher.setOnKeyEventResult(handled, sequence);
+ getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
@@ -2625,6 +2665,149 @@
}
@Override
+ public float getMagnificationScale() {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return 1.0f;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getMagnificationController().getScale();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public Region getMagnifiedRegion() {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return Region.obtain();
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Region region = Region.obtain();
+ getMagnificationController().getMagnifiedRegion(region);
+ return region;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public float getMagnificationCenterX() {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return 0.0f;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getMagnificationController().getCenterX();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public float getMagnificationCenterY() {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return 0.0f;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getMagnificationController().getCenterY();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean resetMagnification(boolean animate) {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return false;
+ }
+ final boolean permissionGranted = mSecurityPolicy.canControlMagnification(this);
+ if (!permissionGranted) {
+ return false;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getMagnificationController().reset(animate);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.USER_CURRENT);
+ if (resolvedUserId != mCurrentUserId) {
+ return false;
+ }
+ final boolean permissionGranted = mSecurityPolicy.canControlMagnification(this);
+ if (!permissionGranted) {
+ return false;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getMagnificationController().setScaleAndCenter(
+ scale, centerX, centerY, animate);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void setMagnificationCallbackEnabled(boolean enabled) {
+ mInvocationHandler.setMagnificationCallbackEnabled(enabled);
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
synchronized (mLock) {
@@ -2700,7 +2883,7 @@
return;
}
mWasConnectedAndDied = true;
- mKeyEventDispatcher.flush();
+ getKeyEventDispatcher().flush(this);
UserState userState = getUserStateLocked(mUserId);
// The death recipient is unregistered in removeServiceLocked
removeServiceLocked(this, userState);
@@ -2809,16 +2992,35 @@
gestureId, 0).sendToTarget();
}
- public void notifyKeyEvent(KeyEvent event, int policyFlags) {
- mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_KEY_EVENT,
- policyFlags, 0, event).sendToTarget();
- }
-
public void notifyClearAccessibilityNodeInfoCache() {
mInvocationHandler.sendEmptyMessage(
InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
}
+ public void notifyMagnificationChanged(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ mInvocationHandler.notifyMagnificationChanged(region, scale, centerX, centerY);
+ }
+
+ /**
+ * Called by the invocation handler to notify the service that the
+ * state of magnification has changed.
+ */
+ private void notifyMagnificationChangedInternal(@NonNull Region region,
+ float scale, float centerX, float centerY) {
+ final IAccessibilityServiceClient listener;
+ synchronized (mLock) {
+ listener = mServiceInterface;
+ }
+ if (listener != null) {
+ try {
+ listener.onMagnificationChanged(region, scale, centerX, centerY);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
+ }
+ }
+ }
+
private void notifyGestureInternal(int gestureId) {
final IAccessibilityServiceClient listener;
synchronized (mLock) {
@@ -2834,10 +3036,6 @@
}
}
- private void notifyKeyEventInternal(KeyEvent event, int policyFlags) {
- mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
- }
-
private void notifyClearAccessibilityCacheInternal() {
final IAccessibilityServiceClient listener;
synchronized (mLock) {
@@ -2955,9 +3153,11 @@
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
- public static final int MSG_ON_KEY_EVENT = 2;
- public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
- public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
+ public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
+
+ private static final int MSG_ON_MAGNIFICATION_CHANGED = 5;
+
+ private boolean mIsMagnificationCallbackEnabled = false;
public InvocationHandler(Looper looper) {
super(looper, null, true);
@@ -2972,19 +3172,17 @@
notifyGestureInternal(gestureId);
} break;
- case MSG_ON_KEY_EVENT: {
- KeyEvent event = (KeyEvent) message.obj;
- final int policyFlags = message.arg1;
- notifyKeyEventInternal(event, policyFlags);
- } break;
-
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
notifyClearAccessibilityCacheInternal();
} break;
- case MSG_ON_KEY_EVENT_TIMEOUT: {
- PendingEvent eventState = (PendingEvent) message.obj;
- setOnKeyEventResult(false, eventState.sequence);
+ case MSG_ON_MAGNIFICATION_CHANGED: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final Region region = (Region) args.arg1;
+ final float scale = (float) args.arg2;
+ final float centerX = (float) args.arg3;
+ final float centerY = (float) args.arg4;
+ notifyMagnificationChangedInternal(region, scale, centerX, centerY);
} break;
default: {
@@ -2992,142 +3190,29 @@
}
}
}
- }
- private final class KeyEventDispatcher {
-
- private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
-
- private PendingEvent mPendingEvents;
-
- private final InputEventConsistencyVerifier mSentEventsVerifier =
- InputEventConsistencyVerifier.isInstrumentationEnabled()
- ? new InputEventConsistencyVerifier(
- this, 0, KeyEventDispatcher.class.getSimpleName()) : null;
-
- public void notifyKeyEvent(KeyEvent event, int policyFlags) {
- final PendingEvent pendingEvent;
-
- synchronized (mLock) {
- pendingEvent = addPendingEventLocked(event, policyFlags);
+ public void notifyMagnificationChanged(@NonNull Region region, float scale,
+ float centerX, float centerY) {
+ if (!mIsMagnificationCallbackEnabled) {
+ // Callback is disabled, don't bother packing args.
+ return;
}
- Message message = mInvocationHandler.obtainMessage(
- InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
- mInvocationHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = region;
+ args.arg2 = scale;
+ args.arg3 = centerX;
+ args.arg4 = centerY;
- try {
- // Accessibility services are exclusively not in the system
- // process, therefore no need to clone the motion event to
- // prevent tampering. It will be cloned in the IPC call.
- mServiceInterface.onKeyEvent(pendingEvent.event, pendingEvent.sequence);
- } catch (RemoteException re) {
- setOnKeyEventResult(false, pendingEvent.sequence);
- }
+ final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
+ msg.sendToTarget();
}
- public void setOnKeyEventResult(boolean handled, int sequence) {
- synchronized (mLock) {
- PendingEvent pendingEvent = removePendingEventLocked(sequence);
- if (pendingEvent != null) {
- mInvocationHandler.removeMessages(
- InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
- pendingEvent);
- pendingEvent.handled = handled;
- finishPendingEventLocked(pendingEvent);
- }
- }
- }
-
- public void flush() {
- synchronized (mLock) {
- cancelAllPendingEventsLocked();
- if (mSentEventsVerifier != null) {
- mSentEventsVerifier.reset();
- }
- }
- }
-
- private PendingEvent addPendingEventLocked(KeyEvent event, int policyFlags) {
- final int sequence = event.getSequenceNumber();
- PendingEvent pendingEvent = obtainPendingEventLocked(event, policyFlags, sequence);
- pendingEvent.next = mPendingEvents;
- mPendingEvents = pendingEvent;
- return pendingEvent;
- }
-
- private PendingEvent removePendingEventLocked(int sequence) {
- PendingEvent previous = null;
- PendingEvent current = mPendingEvents;
-
- while (current != null) {
- if (current.sequence == sequence) {
- if (previous != null) {
- previous.next = current.next;
- } else {
- mPendingEvents = current.next;
- }
- current.next = null;
- return current;
- }
- previous = current;
- current = current.next;
- }
- return null;
- }
-
- private void finishPendingEventLocked(PendingEvent pendingEvent) {
- if (!pendingEvent.handled) {
- sendKeyEventToInputFilter(pendingEvent.event, pendingEvent.policyFlags);
- }
- // Nullify the event since we do not want it to be
- // recycled yet. It will be sent to the input filter.
- pendingEvent.event = null;
- recyclePendingEventLocked(pendingEvent);
- }
-
- private void sendKeyEventToInputFilter(KeyEvent event, int policyFlags) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "Injecting event: " + event);
- }
- if (mSentEventsVerifier != null) {
- mSentEventsVerifier.onKeyEvent(event, 0);
- }
- policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- mMainHandler.obtainMessage(MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER,
- policyFlags, 0, event).sendToTarget();
- }
-
- private void cancelAllPendingEventsLocked() {
- while (mPendingEvents != null) {
- PendingEvent pendingEvent = removePendingEventLocked(mPendingEvents.sequence);
- pendingEvent.handled = false;
- mInvocationHandler.removeMessages(InvocationHandler.MSG_ON_KEY_EVENT_TIMEOUT,
- pendingEvent);
- finishPendingEventLocked(pendingEvent);
- }
+ public void setMagnificationCallbackEnabled(boolean enabled) {
+ mIsMagnificationCallbackEnabled = enabled;
}
}
- }
- private static final class PendingEvent {
- PendingEvent next;
-
- KeyEvent event;
- int policyFlags;
- int sequence;
- boolean handled;
-
- public void clear() {
- if (event != null) {
- event.recycle();
- event = null;
- }
- next = null;
- policyFlags = 0;
- sequence = 0;
- handled = false;
- }
}
final class WindowsForAccessibilityCallback implements
@@ -3660,6 +3745,11 @@
& AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
+ public boolean canControlMagnification(Service service) {
+ return (service.mAccessibilityServiceInfo.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
+ }
+
private int resolveProfileParentLocked(int userId) {
if (userId != mCurrentUserId) {
final long identity = Binder.clearCallingIdentity();
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
new file mode 100644
index 0000000..3469565
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
@@ -0,0 +1,285 @@
+/*
+ ** 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.
+ */
+
+package com.android.server.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Pools;
+import android.util.Pools.Pool;
+import android.util.Slog;
+import android.view.InputEventConsistencyVerifier;
+import android.view.KeyEvent;
+import android.view.WindowManagerPolicy;
+
+import com.android.server.accessibility.AccessibilityManagerService.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Dispatcher to send KeyEvents to all accessibility services that are able to process them.
+ * Events that are handled by one or more services are consumed. Events that are not processed
+ * by any service (or time out before a service reports them as handled) are passed along to
+ * the rest of the system.
+ *
+ * The class assumes that services report their return values in order, which is valid because
+ * they process each call to {@code AccessibilityService.onKeyEvent} on a single thread, and so
+ * don't see the N+1th event until they have processed the Nth event.
+ */
+public class KeyEventDispatcher {
+ // Debugging
+ private static final String LOG_TAG = "KeyEventDispatcher";
+ private static final boolean DEBUG = false;
+ /* KeyEvents must be processed in this time interval */
+ private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
+ private static final int MSG_ON_KEY_EVENT_TIMEOUT = 1;
+ private static final int MAX_POOL_SIZE = 10;
+
+ private final Pool<PendingKeyEvent> mPendingEventPool = new Pools.SimplePool<>(MAX_POOL_SIZE);
+ private final Object mLock;
+
+ /*
+ * Track events sent to each service. If a KeyEvent is to be sent to at least one service,
+ * a corresponding PendingKeyEvent is created for it. This PendingKeyEvent is placed in
+ * the list for each service its KeyEvent is sent to. It is removed from the list when
+ * the service calls setOnKeyEventResult, or when we time out waiting for the service to
+ * respond.
+ */
+ private final Map<Service, ArrayList<PendingKeyEvent>> mPendingEventsMap = new ArrayMap<>();
+
+ private final InputEventConsistencyVerifier mSentEventsVerifier;
+ private final Handler mHandlerToSendKeyEventsToInputFilter;
+ private final int mMessageTypeForSendKeyEvent;
+ private final Handler mKeyEventTimeoutHandler;
+
+ /**
+ * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s
+ * that have not been handled by any accessibility service.
+ * @param messageTypeForSendKeyEvent The field to populate {@code message.what} for the
+ * message that carries a {@code KeyEvent} to be sent to the input filter
+ * @param lock The lock used for all synchronization in this package. This lock must be held
+ * when calling {@code notifyKeyEventLocked}
+ */
+ public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
+ int messageTypeForSendKeyEvent, Object lock) {
+ if (InputEventConsistencyVerifier.isInstrumentationEnabled()) {
+ mSentEventsVerifier = new InputEventConsistencyVerifier(
+ this, 0, KeyEventDispatcher.class.getSimpleName());
+ } else {
+ mSentEventsVerifier = null;
+ }
+ mHandlerToSendKeyEventsToInputFilter = handlerToSendKeyEventsToInputFilter;
+ mMessageTypeForSendKeyEvent = messageTypeForSendKeyEvent;
+ mKeyEventTimeoutHandler =
+ new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback());
+ mLock = lock;
+ }
+
+ /**
+ * Notify that a new KeyEvent is available to accessibility services. Must be called with the
+ * lock used to construct this object held. The boundServices list must also be protected
+ * by a lock.
+ *
+ * @param event The new key event
+ * @param policyFlags Flags for the event
+ * @param boundServices A list of currently bound AccessibilityServices
+ *
+ * @return {@code true} if the event was passed to at least one AccessibilityService,
+ * {@code false} otherwise.
+ */
+ // TODO: The locking policy for boundServices needs some thought.
+ public boolean notifyKeyEventLocked(
+ KeyEvent event, int policyFlags, List<Service> boundServices) {
+ PendingKeyEvent pendingKeyEvent = null;
+ KeyEvent localClone = KeyEvent.obtain(event);
+ for (int i = 0; i < boundServices.size(); i++) {
+ Service service = boundServices.get(i);
+ // Key events are handled only by services that declared
+ // this capability and requested to filter key events.
+ if (!service.mRequestFilterKeyEvents) {
+ continue;
+ }
+ int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
+ & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
+ if (filterKeyEventBit == 0) {
+ continue;
+ }
+
+ try {
+ // The event will be cloned in the IPC call, so it doesn't need to be here.
+ service.mServiceInterface.onKeyEvent(localClone, localClone.getSequenceNumber());
+ } catch (RemoteException re) {
+ continue;
+ }
+
+ if (pendingKeyEvent == null) {
+ pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
+ }
+ ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service);
+ if (pendingEventList == null) {
+ pendingEventList = new ArrayList<>();
+ mPendingEventsMap.put(service, pendingEventList);
+ }
+ pendingEventList.add(pendingKeyEvent);
+ pendingKeyEvent.referenceCount++;
+ }
+
+ if (pendingKeyEvent == null) {
+ localClone.recycle();
+ return false;
+ }
+
+ Message message = mKeyEventTimeoutHandler.obtainMessage(
+ MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
+ mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
+ return true;
+ }
+
+ /**
+ * Set the result from onKeyEvent from one service.
+ *
+ * @param service The service setting the result
+ * @param handled {@code true} if the service handled the {@code KeyEvent}
+ * @param sequence The sequence number of the {@code KeyEvent}
+ */
+ public void setOnKeyEventResult(Service service, boolean handled, int sequence) {
+ synchronized (mLock) {
+ PendingKeyEvent pendingEvent =
+ removeEventFromListLocked(mPendingEventsMap.get(service), sequence);
+ if (pendingEvent != null) {
+ pendingEvent.handled |= handled;
+ removeReferenceToPendingEventLocked(pendingEvent);
+ }
+ }
+ }
+
+ /**
+ * Flush all pending key events for a service, treating all of them as unhandled
+ *
+ * @param service The service for which to flush events
+ */
+ public void flush(Service service) {
+ synchronized (mLock) {
+ List<PendingKeyEvent> pendingEvents = mPendingEventsMap.get(service);
+ if (pendingEvents != null) {
+ for (int i = 0; i < pendingEvents.size(); i++) {
+ PendingKeyEvent pendingEvent = pendingEvents.get(i);
+ removeReferenceToPendingEventLocked(pendingEvent);
+ }
+ mPendingEventsMap.remove(service);
+ }
+ }
+ }
+
+ private PendingKeyEvent obtainPendingEventLocked(KeyEvent event, int policyFlags) {
+ PendingKeyEvent pendingEvent = mPendingEventPool.acquire();
+ if (pendingEvent == null) {
+ pendingEvent = new PendingKeyEvent();
+ }
+ pendingEvent.event = event;
+ pendingEvent.policyFlags = policyFlags;
+ pendingEvent.referenceCount = 0;
+ pendingEvent.handled = false;
+ return pendingEvent;
+ }
+
+ private static PendingKeyEvent removeEventFromListLocked(
+ List<PendingKeyEvent> listOfEvents, int sequence) {
+ /* In normal operation, the event should be first */
+ for (int i = 0; i < listOfEvents.size(); i++) {
+ PendingKeyEvent pendingKeyEvent = listOfEvents.get(i);
+ if (pendingKeyEvent.event.getSequenceNumber() == sequence) {
+ /*
+ * Removing the first element of the ArrayList can be slow if there are a lot
+ * of events backed up, but for a handful of events it's better than incurring
+ * the fixed overhead of LinkedList. An ArrayList optimized for removing the
+ * first element (by treating the underlying array as a circular buffer) would
+ * be ideal.
+ */
+ listOfEvents.remove(pendingKeyEvent);
+ return pendingKeyEvent;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param pendingEvent The event whose reference count should be decreased
+ * @return {@code true} if the event was release, {@code false} if not.
+ */
+ private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) {
+ if (--pendingEvent.referenceCount > 0) {
+ return false;
+ }
+ mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
+ if (!pendingEvent.handled) {
+ /* Pass event to input filter */
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injecting event: " + pendingEvent.event);
+ }
+ if (mSentEventsVerifier != null) {
+ mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0);
+ }
+ int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER;
+ mHandlerToSendKeyEventsToInputFilter
+ .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event)
+ .sendToTarget();
+ } else {
+ pendingEvent.event.recycle();
+ }
+ mPendingEventPool.release(pendingEvent);
+ return true;
+ }
+
+ private static final class PendingKeyEvent {
+ /* Event and policyFlag provided in notifyKeyEventLocked */
+ KeyEvent event;
+ int policyFlags;
+ /*
+ * The referenceCount optimizes the process of determining the number of services
+ * still holding a KeyEvent. It must be equal to the number of times the PendingEvent
+ * appears in mPendingEventsMap, or PendingEvents will leak.
+ */
+ int referenceCount;
+ /* Whether or not at least one service had handled this event */
+ boolean handled;
+ }
+
+ private class Callback implements Handler.Callback {
+ @Override
+ public boolean handleMessage(Message message) {
+ if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) {
+ throw new IllegalArgumentException("Unknown message: " + message.what);
+ }
+ PendingKeyEvent pendingKeyEvent = (PendingKeyEvent) message.obj;
+ synchronized (mLock) {
+ for (ArrayList<PendingKeyEvent> listForService : mPendingEventsMap.values()) {
+ if (listForService.remove(pendingKeyEvent)) {
+ if(removeReferenceToPendingEventLocked(pendingKeyEvent)) {
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index b4411cf..a093d92 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -17,20 +17,36 @@
package com.android.server.accessibility;
import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.MathUtils;
import android.util.Property;
import android.util.Slog;
import android.view.MagnificationSpec;
+import android.view.View;
import android.view.WindowManagerInternal;
import android.view.animation.DecelerateInterpolator;
+import java.util.Locale;
+
/**
* This class is used to control and query the state of display magnification
* from the accessibility manager and related classes. It is responsible for
@@ -38,37 +54,71 @@
* communication between the accessibility manager and window manager.
*/
class MagnificationController {
- private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+ private static final String LOG_TAG = "MagnificationController";
private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
- private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
- private static final String PROPERTY_NAME_MAGNIFICATION_SPEC = "magnificationSpec";
+ private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
- private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
+ private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
+
+ private static final float MIN_SCALE = 1.0f;
+ private static final float MAX_SCALE = 5.0f;
+
+ /**
+ * The minimum scaling factor that can be persisted to secure settings.
+ * This must be > 1.0 to ensure that magnification is actually set to an
+ * enabled state when the scaling factor is restored from settings.
+ */
+ private static final float MIN_PERSISTED_SCALE = 2.0f;
+
+ private final Object mLock = new Object();
+
+ /**
+ * The current magnification spec. If an animation is running, this
+ * reflects the end state.
+ */
private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
- private final Region mMagnifiedBounds = new Region();
+ private final Region mMagnifiedRegion = Region.obtain();
+ private final Region mAvailableRegion = Region.obtain();
+ private final Rect mMagnifiedBounds = new Rect();
+
private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
private final AccessibilityManagerService mAms;
- private final WindowManagerInternal mWindowManager;
- private final ValueAnimator mTransformationAnimator;
+ private final ContentResolver mContentResolver;
+
+ private final ScreenStateObserver mScreenStateObserver;
+ private final WindowStateObserver mWindowStateObserver;
+
+ private final SpecAnimationBridge mSpecAnimationBridge;
+
+ private int mUserId;
public MagnificationController(Context context, AccessibilityManagerService ams) {
mAms = ams;
- mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+ mContentResolver = context.getContentResolver();
+ mScreenStateObserver = new ScreenStateObserver(context, this);
+ mWindowStateObserver = new WindowStateObserver(context, this);
+ mSpecAnimationBridge = new SpecAnimationBridge(context);
+ }
- final Property<MagnificationController, MagnificationSpec> property =
- Property.of(MagnificationController.class, MagnificationSpec.class,
- PROPERTY_NAME_MAGNIFICATION_SPEC);
- final MagnificationSpecEvaluator evaluator = new MagnificationSpecEvaluator();
- final long animationDuration = context.getResources().getInteger(
- R.integer.config_longAnimTime);
- mTransformationAnimator = ObjectAnimator.ofObject(this, property, evaluator,
- mSentMagnificationSpec, mCurrentMagnificationSpec);
- mTransformationAnimator.setDuration(animationDuration);
- mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
+ /**
+ * Registers magnification-related observers.
+ */
+ public void register() {
+ mScreenStateObserver.register();
+ mWindowStateObserver.register();
+ }
+
+ /**
+ * Unregisters magnification-related observers.
+ */
+ public void unregister() {
+ mScreenStateObserver.unregister();
+ mWindowStateObserver.unregister();
}
/**
@@ -80,26 +130,33 @@
}
/**
- * Sets the magnified region.
+ * Sets the magnified and available regions.
*
- * @param region the region to set
- * @param updateSpec {@code true} to update the scale and center based on
- * the region bounds, {@code false} to leave them as-is
+ * @param magnified the magnified region
+ * @param available the region available for magnification
+ * @param updateSpec {@code true} to update the scale and center based on
+ * the region bounds, {@code false} to leave them as-is
*/
- public void setMagnifiedRegion(Region region, boolean updateSpec) {
- mMagnifiedBounds.set(region);
+ public void setMagnifiedRegion(Region magnified, Region available, boolean updateSpec) {
+ synchronized (mLock) {
+ mMagnifiedRegion.set(magnified);
+ mMagnifiedRegion.getBounds(mMagnifiedBounds);
+ mAvailableRegion.set(available);
- if (updateSpec) {
- final Rect magnifiedFrame = mTempRect;
- region.getBounds(magnifiedFrame);
- final float scale = mSentMagnificationSpec.scale;
- final float offsetX = mSentMagnificationSpec.offsetX;
- final float offsetY = mSentMagnificationSpec.offsetY;
- final float centerX = (-offsetX + magnifiedFrame.width() / 2) / scale;
- final float centerY = (-offsetY + magnifiedFrame.height() / 2) / scale;
- setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, false);
- } else {
- mAms.onMagnificationStateChanged();
+ final MagnificationSpec sentSpec = mSpecAnimationBridge.mSentMagnificationSpec;
+ final float scale = sentSpec.scale;
+ final float offsetX = sentSpec.offsetX;
+ final float offsetY = sentSpec.offsetY;
+
+ // Compute the new center and update spec as needed.
+ final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale;
+ final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale;
+ if (updateSpec) {
+ setScaleAndCenter(scale, centerX, centerY, false);
+ } else {
+ mAms.onMagnificationStateChanged();
+ mAms.notifyMagnificationChanged(mMagnifiedRegion, scale, centerX, centerY);
+ }
}
}
@@ -113,18 +170,51 @@
* magnified region, or {@code false} otherwise
*/
public boolean magnifiedRegionContains(float x, float y) {
- return mMagnifiedBounds.contains((int) x, (int) y);
+ synchronized (mLock) {
+ return mMagnifiedRegion.contains((int) x, (int) y);
+ }
}
/**
- * Populates the specified rect with the bounds of the magnified
- * region.
+ * Returns whether the region available for magnification contains the
+ * specified screen-relative coordinates.
+ *
+ * @param x the screen-relative X coordinate to check
+ * @param y the screen-relative Y coordinate to check
+ * @return {@code true} if the coordinate is contained within the
+ * region available for magnification, or {@code false} otherwise
+ */
+ private boolean availableRegionContains(float x, float y) {
+ synchronized (mLock) {
+ return mAvailableRegion.contains((int) x, (int) y);
+ }
+ }
+
+ /**
+ * Populates the specified rect with the screen-relative bounds of the
+ * magnified region. If magnification is not enabled, the returned
+ * bounds will be empty.
*
* @param outBounds rect to populate with the bounds of the magnified
* region
*/
- public void getMagnifiedBounds(Rect outBounds) {
- mMagnifiedBounds.getBounds(outBounds);
+ public void getMagnifiedBounds(@NonNull Rect outBounds) {
+ synchronized (mLock) {
+ outBounds.set(mMagnifiedBounds);
+ }
+ }
+
+ /**
+ * Populates the specified region with the screen-relative magnified
+ * region. If magnification is not enabled, then the returned region
+ * will be empty.
+ *
+ * @param outRegion the region to populate
+ */
+ public void getMagnifiedRegion(@NonNull Region outRegion) {
+ synchronized (mLock) {
+ outRegion.set(mMagnifiedRegion);
+ }
}
/**
@@ -147,6 +237,19 @@
return mCurrentMagnificationSpec.offsetX;
}
+
+ /**
+ * Returns the screen-relative X coordinate of the center of the
+ * magnification viewport.
+ *
+ * @return the X coordinate
+ */
+ public float getCenterX() {
+ synchronized (mLock) {
+ return (mMagnifiedBounds.width() / 2.0f - getOffsetX()) / getScale();
+ }
+ }
+
/**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
@@ -158,6 +261,18 @@
}
/**
+ * Returns the screen-relative Y coordinate of the center of the
+ * magnification viewport.
+ *
+ * @return the Y coordinate
+ */
+ public float getCenterY() {
+ synchronized (mLock) {
+ return (mMagnifiedBounds.height() / 2.0f - getOffsetY()) / getScale();
+ }
+ }
+
+ /**
* Returns the scale currently used by the window manager. If an
* animation is in progress, this reflects the current state of the
* animation.
@@ -165,7 +280,7 @@
* @return the scale currently used by the window manager
*/
public float getSentScale() {
- return mSentMagnificationSpec.scale;
+ return mSpecAnimationBridge.mSentMagnificationSpec.scale;
}
/**
@@ -176,7 +291,7 @@
* @return the X offset currently used by the window manager
*/
public float getSentOffsetX() {
- return mSentMagnificationSpec.offsetX;
+ return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
}
/**
@@ -187,7 +302,7 @@
* @return the Y offset currently used by the window manager
*/
public float getSentOffsetY() {
- return mSentMagnificationSpec.offsetY;
+ return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
}
/**
@@ -196,21 +311,24 @@
*
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
- public void reset(boolean animate) {
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
+ public boolean reset(boolean animate) {
+ synchronized (mLock) {
+ return resetLocked(animate);
}
- mCurrentMagnificationSpec.clear();
- if (animate) {
- animateMagnificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
+ }
+
+ private boolean resetLocked(boolean animate) {
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final boolean changed = !spec.isNop();
+ if (changed) {
+ spec.clear();
}
- final Rect bounds = mTempRect;
- bounds.setEmpty();
- mAms.onMagnificationStateChanged();
+
+ mSpecAnimationBridge.updateSentSpec(spec, animate);
+ return changed;
}
/**
@@ -219,23 +337,32 @@
* transition is immediate.
*
* @param scale the target scale, must be >= 1
+ * @param pivotX the screen-relative X coordinate around which to scale
+ * @param pivotY the screen-relative Y coordinate around which to scale
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
- public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
- final Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final MagnificationSpec spec = mCurrentMagnificationSpec;
- final float oldScale = spec.scale;
- final float oldCenterX = (-spec.offsetX + magnifiedFrame.width() / 2) / oldScale;
- final float oldCenterY = (-spec.offsetY + magnifiedFrame.height() / 2) / oldScale;
- final float normPivotX = (-spec.offsetX + pivotX) / oldScale;
- final float normPivotY = (-spec.offsetY + pivotY) / oldScale;
- final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
- final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
- final float centerX = normPivotX + offsetX;
- final float centerY = normPivotY + offsetY;
- setScaleAndMagnifiedRegionCenter(scale, centerX, centerY, animate);
+ public boolean setScale(float scale, float pivotX, float pivotY, boolean animate) {
+ synchronized (mLock) {
+ // Constrain scale immediately for use in the pivot calculations.
+ scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+
+ final Rect viewport = mTempRect;
+ mMagnifiedRegion.getBounds(viewport);
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.scale;
+ final float oldCenterX = (viewport.width() / 2.0f - spec.offsetX) / oldScale;
+ final float oldCenterY = (viewport.height() / 2.0f - spec.offsetY) / oldScale;
+ final float normPivotX = (pivotX - spec.offsetX) / oldScale;
+ final float normPivotY = (pivotY - spec.offsetY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ return setScaleAndCenterLocked(scale, centerX, centerY, animate);
+ }
}
/**
@@ -248,10 +375,13 @@
* center
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
- public void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
- setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.scale, centerX, centerY,
- animate);
+ public boolean setCenter(float centerX, float centerY, boolean animate) {
+ synchronized (mLock) {
+ return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate);
+ }
}
/**
@@ -259,35 +389,27 @@
* animating the transition. If animation is disabled, the transition
* is immediate.
*
- * @param scale the target scale, must be >= 1
+ * @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to
- * center and scale
+ * center and scale, or {@link Float#NaN} to leave unchanged
* @param centerY the screen-relative Y coordinate around which to
- * center and scale
+ * center and scale, or {@link Float#NaN} to leave unchanged
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
- public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
- boolean animate) {
- if (Float.compare(mCurrentMagnificationSpec.scale, scale) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetX, centerX) == 0
- && Float.compare(mCurrentMagnificationSpec.offsetY, centerY) == 0) {
- return;
+ public boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate) {
+ synchronized (mLock) {
+ return setScaleAndCenterLocked(scale, centerX, centerY, animate);
}
- if (mTransformationAnimator.isRunning()) {
- mTransformationAnimator.cancel();
- }
- if (DEBUG_MAGNIFICATION_CONTROLLER) {
- Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX + " offsetY: " + centerY);
- }
- updateMagnificationSpec(scale, centerX, centerY);
- if (animate) {
- animateMagnificationSpec(mSentMagnificationSpec,
- mCurrentMagnificationSpec);
- } else {
- setMagnificationSpec(mCurrentMagnificationSpec);
- }
- mAms.onMagnificationStateChanged();
+ }
+
+ private boolean setScaleAndCenterLocked(
+ float scale, float centerX, float centerY, boolean animate) {
+ final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
+ mSpecAnimationBridge.updateSentSpec(mCurrentMagnificationSpec, animate);
+ return changed;
}
/**
@@ -297,71 +419,504 @@
* @param offsetY the amount in pixels to offset the Y center
*/
public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) {
- final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
- setMagnificationSpec(mCurrentMagnificationSpec);
+ synchronized (mLock) {
+ final MagnificationSpec currSpec = mCurrentMagnificationSpec;
+ final float nonNormOffsetX = currSpec.offsetX - offsetX;
+ currSpec.offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
+ final float nonNormOffsetY = currSpec.offsetY - offsetY;
+ currSpec.offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
+ mSpecAnimationBridge.updateSentSpec(currSpec, false);
+ }
}
- private void updateMagnificationSpec(float scale, float magnifiedCenterX,
- float magnifiedCenterY) {
- final Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- mCurrentMagnificationSpec.scale = scale;
- final int viewportWidth = magnifiedFrame.width();
- final float nonNormOffsetX = viewportWidth / 2 - magnifiedCenterX * scale;
- mCurrentMagnificationSpec.offsetX = Math.min(Math.max(nonNormOffsetX,
- getMinOffsetX()), 0);
- final int viewportHeight = magnifiedFrame.height();
- final float nonNormOffsetY = viewportHeight / 2 - magnifiedCenterY * scale;
- mCurrentMagnificationSpec.offsetY = Math.min(Math.max(nonNormOffsetY,
- getMinOffsetY()), 0);
+ /**
+ * Persists the current magnification scale to the current user's settings.
+ */
+ public void persistScale() {
+ final float scale = mCurrentMagnificationSpec.scale;
+ final int userId = mUserId;
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Settings.Secure.putFloatForUser(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, userId);
+ return null;
+ }
+ }.execute();
}
- private float getMinOffsetX() {
- final Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportWidth = magnifiedFrame.width();
+ /**
+ * Retrieves a previously persisted magnification scale from the current
+ * user's settings.
+ *
+ * @return the previously persisted magnification scale, or the default
+ * scale if none is available
+ */
+ public float getPersistedScale() {
+ return Settings.Secure.getFloatForUser(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ DEFAULT_MAGNIFICATION_SCALE, mUserId);
+ }
+
+ /**
+ * Updates the current magnification spec.
+ *
+ * @param scale the magnification scale
+ * @param centerX the unscaled, screen-relative X coordinate of the center
+ * of the viewport, or {@link Float#NaN} to leave unchanged
+ * @param centerY the unscaled, screen-relative Y coordinate of the center
+ * of the viewport, or {@link Float#NaN} to leave unchanged
+ * @return {@code true} if the magnification spec changed or {@code false}
+ * otherwise
+ */
+ private boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+ if (!availableRegionContains(centerX, centerY)) {
+ return false;
+ }
+
+ boolean changed = false;
+
+ final MagnificationSpec currSpec = mCurrentMagnificationSpec;
+
+ // Handle scale.
+ if (Float.isNaN(scale)) {
+ scale = getScale();
+ }
+
+ final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ if (Float.compare(currSpec.scale, normScale) != 0) {
+ currSpec.scale = normScale;
+ changed = true;
+ }
+
+ // Handle X offset.
+ if (Float.isNaN(centerX)) {
+ centerX = getCenterX();
+ }
+
+ final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f - centerX * scale;
+ final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
+ if (Float.compare(currSpec.offsetX, offsetX) != 0) {
+ currSpec.offsetX = offsetX;
+ changed = true;
+ }
+
+ // Handle Y offset.
+ if (Float.isNaN(centerY)) {
+ centerY = getCenterY();
+ }
+
+ final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f - centerY * scale;
+ final float offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
+ if (Float.compare(currSpec.offsetY, offsetY) != 0) {
+ currSpec.offsetY = offsetY;
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ private float getMinOffsetXLocked() {
+ final float viewportWidth = mMagnifiedBounds.width();
return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale;
}
- private float getMinOffsetY() {
- final Rect magnifiedFrame = mTempRect;
- mMagnifiedBounds.getBounds(magnifiedFrame);
- final float viewportHeight = magnifiedFrame.height();
+ private float getMinOffsetYLocked() {
+ final float viewportHeight = mMagnifiedBounds.height();
return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale;
}
- private void animateMagnificationSpec(MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- mTransformationAnimator.setObjectValues(fromSpec, toSpec);
- mTransformationAnimator.start();
- }
+ /**
+ * Sets the currently active user ID.
+ *
+ * @param userId the currently active user ID
+ */
+ public void setUserId(int userId) {
+ if (mUserId != userId) {
+ mUserId = userId;
- private void setMagnificationSpec(MagnificationSpec spec) {
- if (DEBUG_SET_MAGNIFICATION_SPEC) {
- Slog.i(LOG_TAG, "Sending: " + spec);
+ synchronized (mLock) {
+ if (isMagnifying()) {
+ reset(false);
+ }
+ }
}
- mSentMagnificationSpec.scale = spec.scale;
- mSentMagnificationSpec.offsetX = spec.offsetX;
- mSentMagnificationSpec.offsetY = spec.offsetY;
- mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
}
- private static class MagnificationSpecEvaluator implements TypeEvaluator<MagnificationSpec> {
- private final MagnificationSpec mTempTransformationSpec = MagnificationSpec.obtain();
+ private boolean isScreenMagnificationAutoUpdateEnabled() {
+ return (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE) == 1);
+ }
+
+ /**
+ * Resets magnification if magnification and auto-update are both enabled.
+ *
+ * @param animate whether the animate the transition
+ * @return {@code true} if magnification was reset to the disabled state,
+ * {@code false} if magnification is still active
+ */
+ boolean resetIfNeeded(boolean animate) {
+ synchronized (mLock) {
+ if (isMagnifying() && isScreenMagnificationAutoUpdateEnabled()) {
+ reset(animate);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
+ final float scale = getSentScale();
+ final float offsetX = getSentOffsetX();
+ final float offsetY = getSentOffsetY();
+ getMagnifiedBounds(outFrame);
+ outFrame.offset((int) -offsetX, (int) -offsetY);
+ outFrame.scale(1.0f / scale);
+ }
+
+ private void requestRectangleOnScreen(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ final Rect magnifiedFrame = mTempRect;
+ getMagnifiedBounds(magnifiedFrame);
+ if (!magnifiedFrame.intersects(left, top, right, bottom)) {
+ return;
+ }
+
+ final Rect magnifFrameInScreenCoords = mTempRect1;
+ getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
+
+ final float scrollX;
+ final float scrollY;
+ if (right - left > magnifFrameInScreenCoords.width()) {
+ final int direction = TextUtils
+ .getLayoutDirectionFromLocale(Locale.getDefault());
+ if (direction == View.LAYOUT_DIRECTION_LTR) {
+ scrollX = left - magnifFrameInScreenCoords.left;
+ } else {
+ scrollX = right - magnifFrameInScreenCoords.right;
+ }
+ } else if (left < magnifFrameInScreenCoords.left) {
+ scrollX = left - magnifFrameInScreenCoords.left;
+ } else if (right > magnifFrameInScreenCoords.right) {
+ scrollX = right - magnifFrameInScreenCoords.right;
+ } else {
+ scrollX = 0;
+ }
+
+ if (bottom - top > magnifFrameInScreenCoords.height()) {
+ scrollY = top - magnifFrameInScreenCoords.top;
+ } else if (top < magnifFrameInScreenCoords.top) {
+ scrollY = top - magnifFrameInScreenCoords.top;
+ } else if (bottom > magnifFrameInScreenCoords.bottom) {
+ scrollY = bottom - magnifFrameInScreenCoords.bottom;
+ } else {
+ scrollY = 0;
+ }
+
+ final float scale = getScale();
+ offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale);
+ }
+ }
+
+ /**
+ * Class responsible for animating spec on the main thread and sending spec
+ * updates to the window manager.
+ */
+ private static class SpecAnimationBridge {
+ private static final int ACTION_UPDATE_SPEC = 1;
+
+ private final Handler mHandler;
+ private final WindowManagerInternal mWindowManager;
+
+ /**
+ * The magnification spec that was sent to the window manager. This should
+ * only be accessed and modified on the main (e.g. animation) thread.
+ */
+ private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
+
+ /**
+ * The animator that updates the sent spec. This should only be accessed
+ * and modified on the main (e.g. animation) thread.
+ */
+ private final ValueAnimator mTransformationAnimator;
+
+ private final long mMainThreadId;
+
+ private SpecAnimationBridge(Context context) {
+ final Looper mainLooper = context.getMainLooper();
+ mMainThreadId = mainLooper.getThread().getId();
+
+ mHandler = new UpdateHandler(context);
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+
+ final MagnificationSpecProperty property = new MagnificationSpecProperty();
+ final MagnificationSpecEvaluator evaluator = new MagnificationSpecEvaluator();
+ final long animationDuration = context.getResources().getInteger(
+ R.integer.config_longAnimTime);
+ mTransformationAnimator = ObjectAnimator.ofObject(this, property, evaluator,
+ mSentMagnificationSpec);
+ mTransformationAnimator.setDuration(animationDuration);
+ mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
+ }
+
+ public void updateSentSpec(MagnificationSpec spec, boolean animate) {
+ if (Thread.currentThread().getId() == mMainThreadId) {
+ // Already on the main thread, don't bother proxying.
+ updateSentSpecInternal(spec, animate);
+ } else {
+ mHandler.obtainMessage(ACTION_UPDATE_SPEC,
+ animate ? 1 : 0, 0, spec).sendToTarget();
+ }
+ }
+
+ /**
+ * Updates the sent spec.
+ */
+ private void updateSentSpecInternal(MagnificationSpec spec, boolean animate) {
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+
+ // If the current and sent specs don't match, update the sent spec.
+ final boolean changed = !mSentMagnificationSpec.equals(spec);
+ if (changed) {
+ if (animate) {
+ animateMagnificationSpec(spec);
+ } else {
+ setMagnificationSpec(spec);
+ }
+ }
+ }
+
+ private void animateMagnificationSpec(MagnificationSpec toSpec) {
+ mTransformationAnimator.setObjectValues(mSentMagnificationSpec, toSpec);
+ mTransformationAnimator.start();
+ }
+
+ private void setMagnificationSpec(MagnificationSpec spec) {
+ if (DEBUG_SET_MAGNIFICATION_SPEC) {
+ Slog.i(LOG_TAG, "Sending: " + spec);
+ }
+
+ mSentMagnificationSpec.setTo(spec);
+ mWindowManager.setMagnificationSpec(spec);
+ }
+
+ private class UpdateHandler extends Handler {
+ public UpdateHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ACTION_UPDATE_SPEC:
+ final boolean animate = msg.arg1 == 1;
+ final MagnificationSpec spec = (MagnificationSpec) msg.obj;
+ updateSentSpecInternal(spec, animate);
+ break;
+ }
+ }
+ }
+
+ private static class MagnificationSpecProperty
+ extends Property<SpecAnimationBridge, MagnificationSpec> {
+ public MagnificationSpecProperty() {
+ super(MagnificationSpec.class, "spec");
+ }
+
+ @Override
+ public MagnificationSpec get(SpecAnimationBridge object) {
+ return object.mSentMagnificationSpec;
+ }
+
+ @Override
+ public void set(SpecAnimationBridge object, MagnificationSpec value) {
+ object.setMagnificationSpec(value);
+ }
+ }
+
+ private static class MagnificationSpecEvaluator
+ implements TypeEvaluator<MagnificationSpec> {
+ private final MagnificationSpec mTempSpec = MagnificationSpec.obtain();
+
+ @Override
+ public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ final MagnificationSpec result = mTempSpec;
+ result.scale = fromSpec.scale + (toSpec.scale - fromSpec.scale) * fraction;
+ result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX) * fraction;
+ result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY) * fraction;
+ return result;
+ }
+ }
+ }
+
+ private static class ScreenStateObserver extends BroadcastReceiver {
+ private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
+
+ private final Context mContext;
+ private final MagnificationController mController;
+ private final Handler mHandler;
+
+ public ScreenStateObserver(Context context, MagnificationController controller) {
+ mContext = context;
+ mController = controller;
+ mHandler = new StateChangeHandler(context);
+ }
+
+ public void register() {
+ mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ }
+
+ public void unregister() {
+ mContext.unregisterReceiver(this);
+ }
@Override
- public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
- MagnificationSpec toSpec) {
- final MagnificationSpec result = mTempTransformationSpec;
- result.scale = fromSpec.scale + (toSpec.scale - fromSpec.scale) * fraction;
- result.offsetX = fromSpec.offsetX + (toSpec.offsetX - fromSpec.offsetX) * fraction;
- result.offsetY = fromSpec.offsetY + (toSpec.offsetY - fromSpec.offsetY) * fraction;
- return result;
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE,
+ intent.getAction()).sendToTarget();
+ }
+
+ private void handleOnScreenStateChange() {
+ mController.resetIfNeeded(false);
+ }
+
+ private class StateChangeHandler extends Handler {
+ public StateChangeHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_ON_SCREEN_STATE_CHANGE:
+ handleOnScreenStateChange();
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This class handles the screen magnification when accessibility is enabled.
+ */
+ private static class WindowStateObserver
+ implements WindowManagerInternal.MagnificationCallbacks {
+ private static final int MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED = 1;
+ private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 2;
+ private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3;
+ private static final int MESSAGE_ON_ROTATION_CHANGED = 4;
+
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
+ private final MagnificationController mController;
+ private final WindowManagerInternal mWindowManager;
+ private final Handler mHandler;
+
+ private boolean mSpecIsDirty;
+
+ public WindowStateObserver(Context context, MagnificationController controller) {
+ mController = controller;
+ mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+ mHandler = new CallbackHandler(context);
+ }
+
+ public void register() {
+ mWindowManager.setMagnificationCallbacks(this);
+ }
+
+ public void unregister() {
+ mWindowManager.setMagnificationCallbacks(null);
+ }
+
+ @Override
+ public void onMagnifiedBoundsChanged(Region magnified, Region available) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = Region.obtain(magnified);
+ args.arg2 = Region.obtain(available);
+ mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
+ }
+
+ private void handleOnMagnifiedBoundsChanged(Region magnified, Region available) {
+ mController.setMagnifiedRegion(magnified, available, mSpecIsDirty);
+ mSpecIsDirty = false;
+ }
+
+ @Override
+ public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = left;
+ args.argi2 = top;
+ args.argi3 = right;
+ args.argi4 = bottom;
+ mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, args).sendToTarget();
+ }
+
+ private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) {
+ mController.requestRectangleOnScreen(left, top, right, bottom);
+ }
+
+ @Override
+ public void onRotationChanged(int rotation) {
+ mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0).sendToTarget();
+ }
+
+ private void handleOnRotationChanged() {
+ // If there was a rotation and magnification is still enabled,
+ // we'll need to rewrite the spec to reflect the new screen
+ // configuration. Conveniently, we'll receive a callback from
+ // the window manager with updated bounds for the magnified
+ // region.
+ mSpecIsDirty = !mController.resetIfNeeded(true);
+ }
+
+ @Override
+ public void onUserContextChanged() {
+ mHandler.sendEmptyMessage(MESSAGE_ON_USER_CONTEXT_CHANGED);
+ }
+
+ private void handleOnUserContextChanged() {
+ mController.resetIfNeeded(true);
+ }
+
+ private class CallbackHandler extends Handler {
+ public CallbackHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final Region magnifiedBounds = (Region) args.arg1;
+ final Region availableBounds = (Region) args.arg2;
+ handleOnMagnifiedBoundsChanged(magnifiedBounds, availableBounds);
+ magnifiedBounds.recycle();
+ availableBounds.recycle();
+ } break;
+ case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final int left = args.argi1;
+ final int top = args.argi2;
+ final int right = args.argi3;
+ final int bottom = args.argi4;
+ handleOnRectangleOnScreenRequested(left, top, right, bottom);
+ args.recycle();
+ } break;
+ case MESSAGE_ON_USER_CONTEXT_CHANGED: {
+ handleOnUserContextChanged();
+ } break;
+ case MESSAGE_ON_ROTATION_CHANGED: {
+ handleOnRotationChanged();
+ } break;
+ }
+ }
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
similarity index 65%
rename from services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
rename to services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 8feb167..51c8ab5 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -16,18 +16,10 @@
package com.android.server.accessibility;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.AsyncTask;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
-import android.provider.Settings;
-import android.text.TextUtils;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
@@ -39,18 +31,12 @@
import android.view.MotionEvent.PointerProperties;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
-import android.view.View;
import android.view.ViewConfiguration;
-import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.os.SomeArgs;
-import com.android.server.LocalServices;
-
-import java.util.Locale;
-
/**
- * This class handles the screen magnification when accessibility is enabled.
+ * This class handles magnification in response to touch events.
+ *
* The behavior is as follows:
*
* 1. Triple tap toggles permanent screen magnification which is magnifying
@@ -88,10 +74,8 @@
*
* 6. The magnification scale will be persisted in settings and in the cloud.
*/
-public final class ScreenMagnifier implements WindowManagerInternal.MagnificationCallbacks,
- EventStreamTransformation {
-
- private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+class MagnificationGestureHandler implements EventStreamTransformation {
+ private static final String LOG_TAG = "MagnificationEventHandler";
private static final boolean DEBUG_STATE_TRANSITIONS = false;
private static final boolean DEBUG_DETECTING = false;
@@ -103,40 +87,19 @@
private static final int STATE_VIEWPORT_DRAGGING = 3;
private static final int STATE_MAGNIFIED_INTERACTION = 4;
- private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
+ private static final float MIN_SCALE = 2.0f;
+ private static final float MAX_SCALE = 5.0f;
- private static final int MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED = 1;
- private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 2;
- private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3;
- private static final int MESSAGE_ON_ROTATION_CHANGED = 4;
-
- private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
-
- private static final int MY_PID = android.os.Process.myPid();
-
- private final Rect mTempRect = new Rect();
- private final Rect mTempRect1 = new Rect();
-
- private final Context mContext;
- private final WindowManagerInternal mWindowManager;
private final MagnificationController mMagnificationController;
- private final ScreenStateObserver mScreenStateObserver;
-
private final DetectingStateHandler mDetectingStateHandler;
- private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler;
+ private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler;
private final StateViewportDraggingHandler mStateViewportDraggingHandler;
- private final int mUserId;
-
- private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
- private final int mMultiTapTimeSlop;
- private final int mTapDistanceSlop;
- private final int mMultiTapDistanceSlop;
-
private EventStreamTransformation mNext;
private int mCurrentState;
private int mPreviousState;
+
private boolean mTranslationEnabledBeforePan;
private PointerCoords[] mTempPointerCoords;
@@ -144,189 +107,44 @@
private long mDelegatingStateDownTime;
- private boolean mUpdateMagnificationSpecOnNextBoundsChange;
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: {
- Region bounds = (Region) message.obj;
- handleOnMagnifiedBoundsChanged(bounds);
- bounds.recycle();
- } break;
- case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
- SomeArgs args = (SomeArgs) message.obj;
- final int left = args.argi1;
- final int top = args.argi2;
- final int right = args.argi3;
- final int bottom = args.argi4;
- handleOnRectangleOnScreenRequested(left, top, right, bottom);
- args.recycle();
- } break;
- case MESSAGE_ON_USER_CONTEXT_CHANGED: {
- handleOnUserContextChanged();
- } break;
- case MESSAGE_ON_ROTATION_CHANGED: {
- final int rotation = message.arg1;
- handleOnRotationChanged(rotation);
- } break;
- }
- }
- };
-
- public ScreenMagnifier(Context context, int userId, int displayId,
- AccessibilityManagerService service) {
- mContext = context;
- mUserId = userId;
- mWindowManager = LocalServices.getService(WindowManagerInternal.class);
-
- mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout()
- + mContext.getResources().getInteger(
- com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
- mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
-
- mDetectingStateHandler = new DetectingStateHandler();
+ public MagnificationGestureHandler(Context context, AccessibilityManagerService ams) {
+ mMagnificationController = ams.getMagnificationController();
+ mDetectingStateHandler = new DetectingStateHandler(context);
mStateViewportDraggingHandler = new StateViewportDraggingHandler();
- mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler(
- context);
-
- mMagnificationController = service.getMagnificationController();
- mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
-
- mWindowManager.setMagnificationCallbacks(this);
+ mMagnifiedContentInteractionStateHandler =
+ new MagnifiedContentInteractionStateHandler(context);
transitionToState(STATE_DETECTING);
}
@Override
- public void onMagnifedBoundsChanged(Region bounds) {
- Region newBounds = Region.obtain(bounds);
- mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, newBounds).sendToTarget();
- if (MY_PID != Binder.getCallingPid()) {
- bounds.recycle();
- }
- }
-
- private void handleOnMagnifiedBoundsChanged(Region bounds) {
- // If there was a rotation we have to update the center of the magnified
- // region since the old offset X/Y may be out of its acceptable range for
- // the new display width and height.
- mMagnificationController.setMagnifiedRegion(
- bounds, mUpdateMagnificationSpecOnNextBoundsChange);
- mUpdateMagnificationSpecOnNextBoundsChange = false;
- }
-
- @Override
- public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = left;
- args.argi2 = top;
- args.argi3 = right;
- args.argi4 = bottom;
- mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, args).sendToTarget();
- }
-
- private void handleOnRectangleOnScreenRequested(int left, int top, int right, int bottom) {
- Rect magnifiedFrame = mTempRect;
- mMagnificationController.getMagnifiedBounds(magnifiedFrame);
- if (!magnifiedFrame.intersects(left, top, right, bottom)) {
- return;
- }
- Rect magnifFrameInScreenCoords = mTempRect1;
- getMagnifiedFrameInContentCoords(magnifFrameInScreenCoords);
- final float scrollX;
- final float scrollY;
- if (right - left > magnifFrameInScreenCoords.width()) {
- final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
- if (direction == View.LAYOUT_DIRECTION_LTR) {
- scrollX = left - magnifFrameInScreenCoords.left;
- } else {
- scrollX = right - magnifFrameInScreenCoords.right;
- }
- } else if (left < magnifFrameInScreenCoords.left) {
- scrollX = left - magnifFrameInScreenCoords.left;
- } else if (right > magnifFrameInScreenCoords.right) {
- scrollX = right - magnifFrameInScreenCoords.right;
- } else {
- scrollX = 0;
- }
- if (bottom - top > magnifFrameInScreenCoords.height()) {
- scrollY = top - magnifFrameInScreenCoords.top;
- } else if (top < magnifFrameInScreenCoords.top) {
- scrollY = top - magnifFrameInScreenCoords.top;
- } else if (bottom > magnifFrameInScreenCoords.bottom) {
- scrollY = bottom - magnifFrameInScreenCoords.bottom;
- } else {
- scrollY = 0;
- }
- final float scale = mMagnificationController.getScale();
- mMagnificationController.offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale);
- }
-
- @Override
- public void onRotationChanged(int rotation) {
- mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0).sendToTarget();
- }
-
- private void handleOnRotationChanged(int rotation) {
- resetMagnificationIfNeeded();
- if (mMagnificationController.isMagnifying()) {
- mUpdateMagnificationSpecOnNextBoundsChange = true;
- }
- }
-
- @Override
- public void onUserContextChanged() {
- mHandler.sendEmptyMessage(MESSAGE_ON_USER_CONTEXT_CHANGED);
- }
-
- private void handleOnUserContextChanged() {
- resetMagnificationIfNeeded();
- }
-
- private void getMagnifiedFrameInContentCoords(Rect rect) {
- final float scale = mMagnificationController.getSentScale();
- final float offsetX = mMagnificationController.getSentOffsetX();
- final float offsetY = mMagnificationController.getSentOffsetY();
- mMagnificationController.getMagnifiedBounds(rect);
- rect.offset((int) -offsetX, (int) -offsetY);
- rect.scale(1.0f / scale);
- }
-
- private void resetMagnificationIfNeeded() {
- if (mMagnificationController.isMagnifying()
- && isScreenMagnificationAutoUpdateEnabled(mContext)) {
- mMagnificationController.reset(true);
- }
- }
-
- @Override
- public void onMotionEvent(MotionEvent event, MotionEvent rawEvent,
- int policyFlags) {
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
}
return;
}
- mMagnifiedContentInteractonStateHandler.onMotionEvent(event);
+ mMagnifiedContentInteractionStateHandler.onMotionEvent(event, rawEvent, policyFlags);
switch (mCurrentState) {
case STATE_DELEGATING: {
handleMotionEventStateDelegating(event, rawEvent, policyFlags);
- } break;
+ }
+ break;
case STATE_DETECTING: {
mDetectingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
- } break;
+ }
+ break;
case STATE_VIEWPORT_DRAGGING: {
- mStateViewportDraggingHandler.onMotionEvent(event, policyFlags);
- } break;
+ mStateViewportDraggingHandler.onMotionEvent(event, rawEvent, policyFlags);
+ }
+ break;
case STATE_MAGNIFIED_INTERACTION: {
// mMagnifiedContentInteractonStateHandler handles events only
// if this is the current state since it uses ScaleGestureDetecotr
// and a GestureDetector which need well formed event stream.
- } break;
+ }
+ break;
default: {
throw new IllegalStateException("Unknown state: " + mCurrentState);
}
@@ -336,7 +154,7 @@
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
if (mNext != null) {
- mNext.onKeyEvent(event, policyFlags);
+ mNext.onKeyEvent(event, policyFlags);
}
}
@@ -366,15 +184,13 @@
@Override
public void onDestroy() {
clear();
- mScreenStateObserver.destroy();
- mWindowManager.setMagnificationCallbacks(null);
}
private void clear() {
mCurrentState = STATE_DETECTING;
mDetectingStateHandler.clear();
mStateViewportDraggingHandler.clear();
- mMagnifiedContentInteractonStateHandler.clear();
+ mMagnifiedContentInteractionStateHandler.clear();
}
private void handleMotionEventStateDelegating(MotionEvent event,
@@ -382,12 +198,14 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDelegatingStateDownTime = event.getDownTime();
- } break;
+ }
+ break;
case MotionEvent.ACTION_UP: {
if (mDetectingStateHandler.mDelayedEventQueue == null) {
transitionToState(STATE_DETECTING);
}
- } break;
+ }
+ break;
}
if (mNext != null) {
// If the event is within the magnified portion of the screen we have
@@ -402,7 +220,8 @@
final float scaledOffsetY = mMagnificationController.getOffsetY();
final int pointerCount = event.getPointerCount();
PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
- PointerProperties[] properties = getTempPointerPropertiesWithMinSize(pointerCount);
+ PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
+ pointerCount);
for (int i = 0; i < pointerCount; i++) {
event.getPointerCoords(i, coords[i]);
coords[i].x = (coords[i].x - scaledOffsetX) / scale;
@@ -441,12 +260,14 @@
}
private PointerProperties[] getTempPointerPropertiesWithMinSize(int size) {
- final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length : 0;
+ final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length
+ : 0;
if (oldSize < size) {
PointerProperties[] oldTempPointerProperties = mTempPointerProperties;
mTempPointerProperties = new PointerProperties[size];
if (oldTempPointerProperties != null) {
- System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0, oldSize);
+ System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0,
+ oldSize);
}
}
for (int i = oldSize; i < size; i++) {
@@ -460,16 +281,20 @@
switch (state) {
case STATE_DELEGATING: {
Slog.i(LOG_TAG, "mCurrentState: STATE_DELEGATING");
- } break;
+ }
+ break;
case STATE_DETECTING: {
Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING");
- } break;
+ }
+ break;
case STATE_VIEWPORT_DRAGGING: {
Slog.i(LOG_TAG, "mCurrentState: STATE_VIEWPORT_DRAGGING");
- } break;
+ }
+ break;
case STATE_MAGNIFIED_INTERACTION: {
Slog.i(LOG_TAG, "mCurrentState: STATE_MAGNIFIED_INTERACTION");
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown state: " + state);
}
@@ -479,20 +304,30 @@
mCurrentState = state;
}
- private final class MagnifiedContentInteractonStateHandler
- extends SimpleOnGestureListener implements OnScaleGestureListener {
- private static final float MIN_SCALE = 1.3f;
- private static final float MAX_SCALE = 5.0f;
+ private interface MotionEventHandler {
+
+ void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
+
+ void clear();
+ }
+
+ /**
+ * This class determines if the user is performing a scale or pan gesture.
+ */
+ private final class MagnifiedContentInteractionStateHandler extends SimpleOnGestureListener
+ implements OnScaleGestureListener, MotionEventHandler {
private final ScaleGestureDetector mScaleGestureDetector;
+
private final GestureDetector mGestureDetector;
private final float mScalingThreshold;
private float mInitialScaleFactor = -1;
+
private boolean mScaling;
- public MagnifiedContentInteractonStateHandler(Context context) {
+ public MagnifiedContentInteractionStateHandler(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
@@ -503,7 +338,8 @@
mGestureDetector = new GestureDetector(context, this);
}
- public void onMotionEvent(MotionEvent event) {
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mScaleGestureDetector.onTouchEvent(event);
mGestureDetector.onTouchEvent(event);
if (mCurrentState != STATE_MAGNIFIED_INTERACTION) {
@@ -511,11 +347,7 @@
}
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
clear();
- final float scale = Math.min(Math.max(mMagnificationController.getScale(),
- MIN_SCALE), MAX_SCALE);
- if (scale != getPersistedScale()) {
- persistScale(scale);
- }
+ mMagnificationController.persistScale();
if (mPreviousState == STATE_VIEWPORT_DRAGGING) {
transitionToState(STATE_VIEWPORT_DRAGGING);
} else {
@@ -552,14 +384,29 @@
}
return false;
}
- final float newScale = mMagnificationController.getScale()
- * detector.getScaleFactor();
- final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE), MAX_SCALE);
- if (DEBUG_SCALING) {
- Slog.i(LOG_TAG, "normalizedNewScale: " + normalizedNewScale);
+
+ final float initialScale = mMagnificationController.getScale();
+ final float targetScale = initialScale * detector.getScaleFactor();
+
+ // Don't allow a gesture to move the user further outside the
+ // desired bounds for gesture-controlled scaling.
+ final float scale;
+ if (targetScale > MAX_SCALE && targetScale > initialScale) {
+ // The target scale is too big and getting bigger.
+ scale = MAX_SCALE;
+ } else if (targetScale < MIN_SCALE && targetScale < initialScale) {
+ // The target scale is too small and getting smaller.
+ scale = MIN_SCALE;
+ } else {
+ // The target scale may be outside our bounds, but at least
+ // it's moving in the right direction. This avoids a "jump" if
+ // we're at odds with some other service's desired bounds.
+ scale = targetScale;
}
- mMagnificationController.setScale(normalizedNewScale, detector.getFocusX(),
- detector.getFocusY(), false);
+
+ final float pivotX = detector.getFocusX();
+ final float pivotY = detector.getFocusY();
+ mMagnificationController.setScale(scale, pivotX, pivotY, false);
return true;
}
@@ -573,16 +420,24 @@
clear();
}
- private void clear() {
+ @Override
+ public void clear() {
mInitialScaleFactor = -1;
mScaling = false;
}
}
- private final class StateViewportDraggingHandler {
+ /**
+ * This class handles motion events when the event dispatcher has
+ * determined that the user is performing a single-finger drag of the
+ * magnification viewport.
+ */
+ private final class StateViewportDraggingHandler implements MotionEventHandler {
+
private boolean mLastMoveOutsideMagnifiedRegion;
- private void onMotionEvent(MotionEvent event, int policyFlags) {
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
@@ -591,7 +446,8 @@
case MotionEvent.ACTION_POINTER_DOWN: {
clear();
transitionToState(STATE_MAGNIFIED_INTERACTION);
- } break;
+ }
+ break;
case MotionEvent.ACTION_MOVE: {
if (event.getPointerCount() != 1) {
throw new IllegalStateException("Should have one pointer down.");
@@ -601,35 +457,43 @@
if (mMagnificationController.magnifiedRegionContains(eventX, eventY)) {
if (mLastMoveOutsideMagnifiedRegion) {
mLastMoveOutsideMagnifiedRegion = false;
- mMagnificationController.setMagnifiedRegionCenter(eventX,
+ mMagnificationController.setCenter(eventX,
eventY, true);
} else {
- mMagnificationController.setMagnifiedRegionCenter(eventX,
+ mMagnificationController.setCenter(eventX,
eventY, false);
}
} else {
mLastMoveOutsideMagnifiedRegion = true;
}
- } break;
+ }
+ break;
case MotionEvent.ACTION_UP: {
if (!mTranslationEnabledBeforePan) {
mMagnificationController.reset(true);
}
clear();
transitionToState(STATE_DETECTING);
- } break;
+ }
+ break;
case MotionEvent.ACTION_POINTER_UP: {
- throw new IllegalArgumentException("Unexpected event type: ACTION_POINTER_UP");
+ throw new IllegalArgumentException(
+ "Unexpected event type: ACTION_POINTER_UP");
}
}
}
+ @Override
public void clear() {
mLastMoveOutsideMagnifiedRegion = false;
}
}
- private final class DetectingStateHandler {
+ /**
+ * This class handles motion events when the event dispatch has not yet
+ * determined what the user is doing. It watches for various tap events.
+ */
+ private final class DetectingStateHandler implements MotionEventHandler {
private static final int MESSAGE_ON_ACTION_TAP_AND_HOLD = 1;
@@ -637,12 +501,30 @@
private static final int ACTION_TAP_COUNT = 3;
+ private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
+
+ private final int mMultiTapTimeSlop;
+
+ private final int mTapDistanceSlop;
+
+ private final int mMultiTapDistanceSlop;
+
private MotionEventInfo mDelayedEventQueue;
private MotionEvent mLastDownEvent;
+
private MotionEvent mLastTapUpEvent;
+
private int mTapCount;
+ public DetectingStateHandler(Context context) {
+ mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout()
+ + context.getResources().getInteger(
+ com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
+ mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message message) {
@@ -652,12 +534,14 @@
MotionEvent event = (MotionEvent) message.obj;
final int policyFlags = message.arg1;
onActionTapAndHold(event, policyFlags);
- } break;
+ }
+ break;
case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
transitionToState(STATE_DELEGATING);
sendDelayedMotionEvents();
clear();
- } break;
+ }
+ break;
default: {
throw new IllegalArgumentException("Unknown message type: " + type);
}
@@ -665,6 +549,7 @@
}
};
+ @Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
final int action = event.getActionMasked();
@@ -678,7 +563,7 @@
}
if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null
&& GestureUtils.isMultiTap(mLastDownEvent, event,
- mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
policyFlags, 0, event);
mHandler.sendMessageDelayed(message,
@@ -690,7 +575,8 @@
}
clearLastDownEvent();
mLastDownEvent = MotionEvent.obtain(event);
- } break;
+ }
+ break;
case MotionEvent.ACTION_POINTER_DOWN: {
if (mMagnificationController.isMagnifying()) {
transitionToState(STATE_MAGNIFIED_INTERACTION);
@@ -698,7 +584,8 @@
} else {
transitionToDelegatingStateAndClear();
}
- } break;
+ }
+ break;
case MotionEvent.ACTION_MOVE: {
if (mLastDownEvent != null && mTapCount < ACTION_TAP_COUNT - 1) {
final double distance = GestureUtils.computeDistance(mLastDownEvent,
@@ -707,7 +594,8 @@
transitionToDelegatingStateAndClear();
}
}
- } break;
+ }
+ break;
case MotionEvent.ACTION_UP: {
if (mLastDownEvent == null) {
return;
@@ -715,8 +603,8 @@
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
if (!mMagnificationController.magnifiedRegionContains(
event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
- return;
+ transitionToDelegatingStateAndClear();
+ return;
}
if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
mTapDistanceSlop, 0)) {
@@ -739,13 +627,16 @@
}
clearLastTapUpEvent();
mLastTapUpEvent = MotionEvent.obtain(event);
- } break;
+ }
+ break;
case MotionEvent.ACTION_POINTER_UP: {
/* do nothing */
- } break;
+ }
+ break;
}
}
+ @Override
public void clear() {
mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
@@ -792,7 +683,7 @@
while (mDelayedEventQueue != null) {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
- ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mRawEvent,
+ MagnificationGestureHandler.this.onMotionEvent(info.mEvent, info.mRawEvent,
info.mPolicyFlags);
info.recycle();
}
@@ -816,9 +707,11 @@
if (DEBUG_DETECTING) {
Slog.i(LOG_TAG, "onActionTap()");
}
+
if (!mMagnificationController.isMagnifying()) {
- mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
- up.getX(), up.getY(), true);
+ final float targetScale = mMagnificationController.getPersistedScale();
+ final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
+ mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true);
} else {
mMagnificationController.reset(true);
}
@@ -828,50 +721,36 @@
if (DEBUG_DETECTING) {
Slog.i(LOG_TAG, "onActionTapAndHold()");
}
+
clear();
mTranslationEnabledBeforePan = mMagnificationController.isMagnifying();
- mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
- down.getX(), down.getY(), true);
+
+ final float targetScale = mMagnificationController.getPersistedScale();
+ final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE);
+ mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true);
+
transitionToState(STATE_VIEWPORT_DRAGGING);
}
}
- private void persistScale(final float scale) {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- Settings.Secure.putFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, mUserId);
- return null;
- }
- }.execute();
- }
-
- private float getPersistedScale() {
- return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
- DEFAULT_MAGNIFICATION_SCALE, mUserId);
- }
-
- private static boolean isScreenMagnificationAutoUpdateEnabled(Context context) {
- return (Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
- DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE) == 1);
- }
-
private static final class MotionEventInfo {
private static final int MAX_POOL_SIZE = 10;
private static final Object sLock = new Object();
+
private static MotionEventInfo sPool;
+
private static int sPoolSize;
private MotionEventInfo mNext;
+
private boolean mInPool;
public MotionEvent mEvent;
+
public MotionEvent mRawEvent;
+
public int mPolicyFlags;
public static MotionEventInfo obtain(MotionEvent event, MotionEvent rawEvent,
@@ -922,47 +801,4 @@
mPolicyFlags = 0;
}
}
-
- private final class ScreenStateObserver extends BroadcastReceiver {
- private static final int MESSAGE_ON_SCREEN_STATE_CHANGE = 1;
-
- private final Context mContext;
- private final MagnificationController mMagnificationController;
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_ON_SCREEN_STATE_CHANGE: {
- String action = (String) message.obj;
- handleOnScreenStateChange(action);
- } break;
- }
- }
- };
-
- public ScreenStateObserver(Context context,
- MagnificationController magnificationController) {
- mContext = context;
- mMagnificationController = magnificationController;
- mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
- }
-
- public void destroy() {
- mContext.unregisterReceiver(this);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(MESSAGE_ON_SCREEN_STATE_CHANGE,
- intent.getAction()).sendToTarget();
- }
-
- private void handleOnScreenStateChange(String action) {
- if (mMagnificationController.isMagnifying()
- && isScreenMagnificationAutoUpdateEnabled(mContext)) {
- mMagnificationController.reset(false);
- }
- }
- }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 02b5b8a..25fef18 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -79,10 +79,11 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
-
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
+
import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -1954,8 +1955,13 @@
if (period < MIN_UPDATE_PERIOD) {
period = MIN_UPDATE_PERIOD;
}
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, provider.broadcast);
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + period, period, provider.broadcast);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index a5ddc12..f6af942 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -25,6 +25,7 @@
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
import android.app.IAlarmManager;
+import android.app.IUidObserver;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -41,6 +42,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -115,6 +117,7 @@
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
+ DeviceIdleController.LocalService mLocalDeviceIdleController;
final Object mLock = new Object();
@@ -458,7 +461,7 @@
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
- for (int i = 0; i < alarms.size(); ) {
+ for (int i = alarms.size()-1; i >= 0; i--) {
Alarm alarm = alarms.get(i);
if (alarm.matches(packageName)) {
alarms.remove(i);
@@ -474,7 +477,42 @@
newEnd = alarm.maxWhenElapsed;
}
newFlags |= alarm.flags;
- i++;
+ }
+ }
+ if (didRemove) {
+ // commit the new batch bounds
+ start = newStart;
+ end = newEnd;
+ flags = newFlags;
+ }
+ return didRemove;
+ }
+
+ boolean removeForStopped(final int uid) {
+ boolean didRemove = false;
+ long newStart = 0; // recalculate endpoints as we go
+ long newEnd = Long.MAX_VALUE;
+ int newFlags = 0;
+ for (int i = alarms.size()-1; i >= 0; i--) {
+ Alarm alarm = alarms.get(i);
+ try {
+ if (alarm.uid == uid && ActivityManagerNative.getDefault().getAppStartMode(
+ uid, alarm.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
+ alarms.remove(i);
+ didRemove = true;
+ if (alarm.alarmClock != null) {
+ mNextAlarmClockMayChange = true;
+ }
+ } else {
+ if (alarm.whenElapsed > newStart) {
+ newStart = alarm.whenElapsed;
+ }
+ if (alarm.maxWhenElapsed < newEnd) {
+ newEnd = alarm.maxWhenElapsed;
+ }
+ newFlags |= alarm.flags;
+ }
+ } catch (RemoteException e) {
}
}
if (didRemove) {
@@ -889,6 +927,13 @@
Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
publishBinderService(Context.ALARM_SERVICE, mService);
}
@@ -897,6 +942,8 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mConstants.start(getContext().getContentResolver());
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mLocalDeviceIdleController
+ = LocalServices.getService(DeviceIdleController.LocalService.class);
}
}
@@ -1032,6 +1079,15 @@
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
callingUid, callingPackage);
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(callingUid, callingPackage)
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
removeLocked(operation, directReceiver);
setImplLocked(a, false, doValidate);
}
@@ -1838,6 +1894,37 @@
}
}
+ void removeForStoppedLocked(int uid) {
+ boolean didRemove = false;
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ didRemove |= b.removeForStopped(uid);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
+ }
+ }
+ for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ try {
+ if (a.uid == uid && ActivityManagerNative.getDefault().getAppStartMode(
+ uid, a.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingWhileIdleAlarms.remove(i);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (didRemove) {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "remove(package) changed bounds; rebatching");
+ }
+ rebatchAllAlarmsLocked(true);
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
void removeUserLocked(int userHandle) {
boolean didRemove = false;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
@@ -2164,7 +2251,7 @@
public boolean matches(PendingIntent pi, IAlarmListener rec) {
return (operation != null)
? operation.equals(pi)
- : listener.asBinder().equals(rec.asBinder());
+ : rec != null && listener.asBinder().equals(rec.asBinder());
}
public boolean matches(String packageName) {
@@ -2468,10 +2555,9 @@
private class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
- public static final int MINUTE_CHANGE_EVENT = 2;
- public static final int DATE_CHANGE_EVENT = 3;
- public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 4;
- public static final int LISTENER_TIMEOUT = 5;
+ public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
+ public static final int LISTENER_TIMEOUT = 3;
+ public static final int REPORT_ALARMS_ACTIVE = 4;
public AlarmHandler() {
}
@@ -2511,6 +2597,12 @@
mDeliveryTracker.alarmTimedOut((IBinder) msg.obj);
break;
+ case REPORT_ALARMS_ACTIVE:
+ if (mLocalDeviceIdleController != null) {
+ mLocalDeviceIdleController.setAlarmsActive(msg.arg1 != 0);
+ }
+ break;
+
default:
// nope, just ignore it
break;
@@ -2665,7 +2757,24 @@
}
}
}
-
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ synchronized (mLock) {
+ removeForStoppedLocked(uid);
+ }
+ }
+ };
+
private final BroadcastStats getStatsLocked(PendingIntent pi) {
String pkg = pi.getCreatorPackage();
int uid = pi.getCreatorUid();
@@ -2740,6 +2849,7 @@
}
mBroadcastRefCount--;
if (mBroadcastRefCount == 0) {
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 0).sendToTarget();
mWakeLock.release();
if (mInFlight.size() > 0) {
mLog.w("Finished all dispatches with " + mInFlight.size()
@@ -2873,6 +2983,7 @@
alarm.type, alarm.statsTag, (alarm.operation == null) ? alarm.uid : -1,
true);
mWakeLock.acquire();
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1).sendToTarget();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
alarm.operation, alarm.listener, alarm.workSource, alarm.uid,
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 96c1e2a..a5cef1a 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -47,14 +47,15 @@
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.storage.MountServiceInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -557,12 +558,12 @@
ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
try {
if (reportedPackageNames == null) {
- callback.mCallback.opChanged(code, null);
+ callback.mCallback.opChanged(code, uid, null);
} else {
final int reportedPackageCount = reportedPackageNames.size();
for (int j = 0; j < reportedPackageCount; j++) {
String reportedPackageName = reportedPackageNames.valueAt(j);
- callback.mCallback.opChanged(code, reportedPackageName);
+ callback.mCallback.opChanged(code, uid, reportedPackageName);
}
}
} catch (RemoteException e) {
@@ -620,7 +621,7 @@
try {
for (int i = 0; i < repCbs.size(); i++) {
try {
- repCbs.get(i).mCallback.opChanged(code, packageName);
+ repCbs.get(i).mCallback.opChanged(code, uid, packageName);
} catch (RemoteException e) {
}
}
@@ -630,39 +631,51 @@
}
}
- private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
- HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
- String packageName, int op, ArrayList<Callback> cbs) {
+ private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
+ HashMap<Callback, ArrayList<ChangeRec>> callbacks,
+ int op, int uid, String packageName, ArrayList<Callback> cbs) {
if (cbs == null) {
return callbacks;
}
if (callbacks == null) {
- callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
+ callbacks = new HashMap<>();
}
boolean duplicate = false;
for (int i=0; i<cbs.size(); i++) {
Callback cb = cbs.get(i);
- ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
+ ArrayList<ChangeRec> reports = callbacks.get(cb);
if (reports == null) {
- reports = new ArrayList<Pair<String, Integer>>();
+ reports = new ArrayList<>();
callbacks.put(cb, reports);
} else {
final int reportCount = reports.size();
for (int j = 0; j < reportCount; j++) {
- Pair<String, Integer> report = reports.get(j);
- if (report.second == op && report.first.equals(packageName)) {
+ ChangeRec report = reports.get(j);
+ if (report.op == op && report.pkg.equals(packageName)) {
duplicate = true;
break;
}
}
}
if (!duplicate) {
- reports.add(new Pair<>(packageName, op));
+ reports.add(new ChangeRec(op, uid, packageName));
}
}
return callbacks;
}
+ static final class ChangeRec {
+ final int op;
+ final int uid;
+ final String pkg;
+
+ ChangeRec(int _op, int _uid, String _pkg) {
+ op = _op;
+ uid = _uid;
+ pkg = _pkg;
+ }
+ }
+
@Override
public void resetAllModes(int reqUserId, String reqPackageName) {
final int callingPid = Binder.getCallingPid();
@@ -682,7 +695,7 @@
}
}
- HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
+ HashMap<Callback, ArrayList<ChangeRec>> callbacks = null;
synchronized (this) {
boolean changed = false;
for (int i = mUidStates.size() - 1; i >= 0; i--) {
@@ -699,9 +712,9 @@
uidState.opModes = null;
}
for (String packageName : getPackagesForUid(uidState.uid)) {
- callbacks = addCallbacks(callbacks, packageName, code,
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mOpModeWatchers.get(code));
- callbacks = addCallbacks(callbacks, packageName, code,
+ callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mPackageModeWatchers.get(packageName));
}
}
@@ -734,9 +747,9 @@
&& curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
changed = true;
- callbacks = addCallbacks(callbacks, packageName, curOp.op,
+ callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mOpModeWatchers.get(curOp.op));
- callbacks = addCallbacks(callbacks, packageName, curOp.op,
+ callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mPackageModeWatchers.get(packageName));
if (curOp.time == 0 && curOp.rejectTime == 0) {
pkgOps.removeAt(j);
@@ -757,13 +770,13 @@
}
}
if (callbacks != null) {
- for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
+ for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
Callback cb = ent.getKey();
- ArrayList<Pair<String, Integer>> reports = ent.getValue();
+ ArrayList<ChangeRec> reports = ent.getValue();
for (int i=0; i<reports.size(); i++) {
- Pair<String, Integer> rep = reports.get(i);
+ ChangeRec rep = reports.get(i);
try {
- cb.mCallback.opChanged(rep.second, rep.first);
+ cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg);
} catch (RemoteException e) {
}
}
@@ -1163,8 +1176,10 @@
if (pkgUid != uid) {
// Oops! The package name is not valid for the uid they are calling
// under. Abort.
+ RuntimeException ex = new RuntimeException("here");
+ ex.fillInStackTrace();
Slog.w(TAG, "Bad call: specified package " + packageName
- + " under uid " + uid + " but it is really " + pkgUid);
+ + " under uid " + uid + " but it is really " + pkgUid, ex);
return null;
}
} finally {
@@ -1541,15 +1556,300 @@
}
}
- private void dumpHelp(PrintWriter pw) {
- pw.println("AppOps service (appops) dump options:");
- pw.println(" [-h] [CMD]");
- pw.println(" -h: print this help text.");
- pw.println("Commands:");
+ static class Shell extends ShellCommand {
+ final IAppOpsService mInterface;
+ final AppOpsService mInternal;
+
+ int userId = UserHandle.USER_SYSTEM;
+ String packageName;
+ String opStr;
+ int op;
+ int packageUid;
+
+ Shell(IAppOpsService iface, AppOpsService internal) {
+ mInterface = iface;
+ mInternal = internal;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ dumpCommandHelp(pw);
+ }
+
+ private int strOpToOp(String op, PrintWriter err) {
+ try {
+ return AppOpsManager.strOpToOp(op);
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ return Integer.parseInt(op);
+ } catch (NumberFormatException e) {
+ }
+ try {
+ return AppOpsManager.strDebugOpToOp(op);
+ } catch (IllegalArgumentException e) {
+ err.println("Error: " + e.getMessage());
+ return -1;
+ }
+ }
+
+ int parseUserPackageOp(boolean reqOp, PrintWriter err) throws RemoteException {
+ userId = UserHandle.USER_CURRENT;
+ packageName = null;
+ opStr = null;
+ for (String argument; (argument = getNextArg()) != null;) {
+ if ("--user".equals(argument)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ if (packageName == null) {
+ packageName = argument;
+ } else if (opStr == null) {
+ opStr = argument;
+ break;
+ }
+ }
+ }
+ if (packageName == null) {
+ err.println("Error: Package name not specified.");
+ return -1;
+ } else if (opStr == null && reqOp) {
+ err.println("Error: Operation not specified.");
+ return -1;
+ }
+ if (opStr != null) {
+ op = strOpToOp(opStr, err);
+ if (op < 0) {
+ return -1;
+ }
+ } else {
+ op = AppOpsManager.OP_NONE;
+ }
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+ if ("root".equals(packageName)) {
+ packageUid = 0;
+ } else {
+ packageUid = AppGlobals.getPackageManager().getPackageUid(packageName, userId);
+ }
+ if (packageUid < 0) {
+ err.println("Error: No UID for " + packageName + " in user " + userId);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell(this, this)).exec(this, in, out, err, args, resultReceiver);
+ }
+
+ static void dumpCommandHelp(PrintWriter pw) {
+ pw.println("AppOps service (appops) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" set [--user <USER_ID>] <PACKAGE> <OP> <MODE>");
+ pw.println(" Set the mode for a particular application and operation.");
+ pw.println(" get [--user <USER_ID>] <PACKAGE> [<OP>]");
+ pw.println(" Return the mode for a particular application and optional operation.");
+ pw.println(" reset [--user <USER_ID>] [<PACKAGE>]");
+ pw.println(" Reset the given application or all applications to default modes.");
pw.println(" write-settings");
pw.println(" Immediately write pending changes to storage.");
pw.println(" read-settings");
pw.println(" Read the last written settings, replacing current state in RAM.");
+ pw.println(" options:");
+ pw.println(" <PACKAGE> an Android package name.");
+ pw.println(" <OP> an AppOps operation.");
+ pw.println(" <MODE> one of allow, ignore, deny, or default");
+ pw.println(" <USER_ID> the user id under which the package is installed. If --user is not");
+ pw.println(" specified, the current user is assumed.");
+ }
+
+ static int onShellCommand(Shell shell, String cmd) {
+ if (cmd == null) {
+ return shell.handleDefaultCommands(cmd);
+ }
+ PrintWriter pw = shell.getOutPrintWriter();
+ PrintWriter err = shell.getErrPrintWriter();
+ try {
+ switch (cmd) {
+ case "set": {
+ int res = shell.parseUserPackageOp(true, err);
+ if (res < 0) {
+ return res;
+ }
+ String modeStr = shell.getNextArg();
+ if (modeStr == null) {
+ err.println("Error: Mode not specified.");
+ return -1;
+ }
+
+ final int mode;
+ switch (modeStr) {
+ case "allow":
+ mode = AppOpsManager.MODE_ALLOWED;
+ break;
+ case "deny":
+ mode = AppOpsManager.MODE_ERRORED;
+ break;
+ case "ignore":
+ mode = AppOpsManager.MODE_IGNORED;
+ break;
+ case "default":
+ mode = AppOpsManager.MODE_DEFAULT;
+ break;
+ default:
+ err.println("Error: Mode " + modeStr + " is not valid,");
+ return -1;
+ }
+
+ shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode);
+ return 0;
+ }
+ case "get": {
+ int res = shell.parseUserPackageOp(false, err);
+ if (res < 0) {
+ return res;
+ }
+
+ List<AppOpsManager.PackageOps> ops = shell.mInterface.getOpsForPackage(
+ shell.packageUid, shell.packageName,
+ shell.op != AppOpsManager.OP_NONE ? new int[] {shell.op} : null);
+ if (ops == null || ops.size() <= 0) {
+ pw.println("No operations.");
+ return 0;
+ }
+ final long now = System.currentTimeMillis();
+ for (int i=0; i<ops.size(); i++) {
+ List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+ for (int j=0; j<entries.size(); j++) {
+ AppOpsManager.OpEntry ent = entries.get(j);
+ pw.print(AppOpsManager.opToName(ent.getOp()));
+ pw.print(": ");
+ switch (ent.getMode()) {
+ case AppOpsManager.MODE_ALLOWED:
+ pw.print("allow");
+ break;
+ case AppOpsManager.MODE_IGNORED:
+ pw.print("ignore");
+ break;
+ case AppOpsManager.MODE_ERRORED:
+ pw.print("deny");
+ break;
+ case AppOpsManager.MODE_DEFAULT:
+ pw.print("default");
+ break;
+ default:
+ pw.print("mode=");
+ pw.print(ent.getMode());
+ break;
+ }
+ if (ent.getTime() != 0) {
+ pw.print("; time=");
+ TimeUtils.formatDuration(now - ent.getTime(), pw);
+ pw.print(" ago");
+ }
+ if (ent.getRejectTime() != 0) {
+ pw.print("; rejectTime=");
+ TimeUtils.formatDuration(now - ent.getRejectTime(), pw);
+ pw.print(" ago");
+ }
+ if (ent.getDuration() == -1) {
+ pw.print(" (running)");
+ } else if (ent.getDuration() != 0) {
+ pw.print("; duration=");
+ TimeUtils.formatDuration(ent.getDuration(), pw);
+ }
+ pw.println();
+ }
+ }
+ return 0;
+ }
+ case "reset": {
+ String packageName = null;
+ int userId = UserHandle.USER_CURRENT;
+ for (String argument; (argument = shell.getNextArg()) != null;) {
+ if ("--user".equals(argument)) {
+ String userStr = shell.getNextArgRequired();
+ userId = UserHandle.parseUserArg(userStr);
+ } else {
+ if (packageName == null) {
+ packageName = argument;
+ } else {
+ err.println("Error: Unsupported argument: " + argument);
+ return -1;
+ }
+ }
+ }
+
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+
+ shell.mInterface.resetAllModes(userId, packageName);
+ pw.print("Reset all modes for: ");
+ if (userId == UserHandle.USER_ALL) {
+ pw.print("all users");
+ } else {
+ pw.print("user "); pw.print(userId);
+ }
+ pw.print(", ");
+ if (packageName == null) {
+ pw.println("all packages");
+ } else {
+ pw.print("package "); pw.println(packageName);
+ }
+ return 0;
+ }
+ case "write-settings": {
+ shell.mInternal.mContext.enforcePermission(
+ android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (shell.mInternal) {
+ shell.mInternal.mHandler.removeCallbacks(shell.mInternal.mWriteRunner);
+ }
+ shell.mInternal.writeState();
+ pw.println("Current settings written.");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return 0;
+ }
+ case "read-settings": {
+ shell.mInternal.mContext.enforcePermission(
+ android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ long token = Binder.clearCallingIdentity();
+ try {
+ shell.mInternal.readState();
+ pw.println("Last settings read.");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return 0;
+ }
+ default:
+ return shell.handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private void dumpHelp(PrintWriter pw) {
+ pw.println("AppOps service (appops) dump options:");
+ pw.println(" none");
}
@Override
@@ -1570,27 +1870,6 @@
return;
} else if ("-a".equals(arg)) {
// dump all data
- } else if ("write-settings".equals(arg)) {
- long token = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- mHandler.removeCallbacks(mWriteRunner);
- }
- writeState();
- pw.println("Current settings written.");
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return;
- } else if ("read-settings".equals(arg)) {
- long token = Binder.clearCallingIdentity();
- try {
- readState();
- pw.println("Last settings read.");
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
return;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index fd67b41..f329cff 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -95,8 +95,6 @@
private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
private static final int MESSAGE_TIMEOUT_BIND =100;
private static final int MESSAGE_TIMEOUT_UNBIND =101;
- private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
- private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
@@ -587,15 +585,6 @@
}
}
- /** @hide*/
- public void getNameAndAddress() {
- if (DBG) {
- Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
- " mBinding = " + mBinding);
- }
- Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(msg);
- }
public boolean enableNoAutoConnect()
{
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -681,14 +670,13 @@
if (mUnbinding) return;
mUnbinding = true;
if (mBluetooth != null) {
- if (!mConnection.isGetNameAddressOnly()) {
- //Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothCallback",re);
- }
+ //Unregister callback object
+ try {
+ mBluetooth.unregisterCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister BluetoothCallback",re);
}
+
if (DBG) Log.d(TAG, "Sending unbind request.");
mBluetooth = null;
//Unbind
@@ -780,11 +768,6 @@
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
sendEnableMsg(mQuietEnableExternal);
}
- if (!isNameAndAddressSet()) {
- // Sync the Bluetooth name and address from the Bluetooth Adapter
- if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address...");
- getNameAndAddress();
- }
}
/**
@@ -957,42 +940,38 @@
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
/**
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
@@ -1052,17 +1031,6 @@
}
private class BluetoothServiceConnection implements ServiceConnection {
-
- private boolean mGetNameAddressOnly;
-
- public void setGetNameAddressOnly(boolean getOnly) {
- mGetNameAddressOnly = getOnly;
- }
-
- public boolean isGetNameAddressOnly() {
- return mGetNameAddressOnly;
- }
-
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
@@ -1108,104 +1076,6 @@
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
- case MESSAGE_GET_NAME_AND_ADDRESS: {
- if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- //Start bind request
- if ((mBluetooth == null) && (!mBinding)) {
- if (DBG) Log.d(TAG, "Binding to service to get name and address");
- mConnection.setGetNameAddressOnly(true);
- //Start bind timeout and bind
- Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
- mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- Intent i = new Intent(IBluetooth.class.getName());
- if (!doBind(i, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT)) {
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
- } else {
- mBinding = true;
- }
- }
- else {
- Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- saveMsg.arg1 = 0;
- if (mBluetooth != null) {
- mHandler.sendMessage(saveMsg);
- } else {
- // if enable is also called to bind the service
- // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
- mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
- }
- }
- }
- break;
- }
- case MESSAGE_SAVE_NAME_AND_ADDRESS: {
- boolean unbind = false;
- if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.enable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
- }
- }
- }
- if (mBluetooth != null && !mConnection.isGetNameAddressOnly()) waitForOnOff(true, false);
- synchronized(mConnection) {
- if (mBluetooth != null) {
- String name = null;
- String address = null;
- try {
- name = mBluetooth.getName();
- address = mBluetooth.getAddress();
- } catch (RemoteException re) {
- Log.e(TAG,"",re);
- }
-
- if (name != null && address != null) {
- storeNameAndAddress(name,address);
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- } else {
- if (msg.arg1 < MAX_SAVE_RETRIES) {
- Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- retryMsg.arg1= 1+msg.arg1;
- if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
- mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
- } else {
- Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- }
- }
- if (!mEnable && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.disable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call disable()",e);
- }
- }
- } else {
- // rebind service by Request GET NAME AND ADDRESS
- // if service is unbinded by disable or
- // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- }
- }
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- waitForOnOff(false, true);
- }
- if (unbind) {
- unbindAndFinish();
- }
- break;
- }
case MESSAGE_ENABLE:
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
@@ -1308,14 +1178,6 @@
Log.e(TAG,"Unable to call configHciSnoopLog", e);
}
- if (mConnection.isGetNameAddressOnly()) {
- //Request GET NAME AND ADDRESS
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- if (!mEnable) return;
- }
-
- mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
@@ -1412,25 +1274,23 @@
SERVICE_RESTART_TIME_MS);
}
- if (!mConnection.isGetNameAddressOnly()) {
- sendBluetoothServiceDownCallback();
+ sendBluetoothServiceDownCallback();
- // Send BT state broadcast to update
- // the BT icon correctly
- if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
- (mState == BluetoothAdapter.STATE_ON)) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
- BluetoothAdapter.STATE_TURNING_OFF);
- mState = BluetoothAdapter.STATE_TURNING_OFF;
- }
- if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_OFF);
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
+ (mState == BluetoothAdapter.STATE_ON)) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+ BluetoothAdapter.STATE_TURNING_OFF);
+ mState = BluetoothAdapter.STATE_TURNING_OFF;
}
+ if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
+ BluetoothAdapter.STATE_OFF);
+ }
+
+ mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+ mState = BluetoothAdapter.STATE_OFF;
break;
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -1539,7 +1399,6 @@
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
UserHandle.CURRENT)) {
@@ -1548,21 +1407,6 @@
mBinding = true;
}
} else if (mBluetooth != null) {
- if (mConnection.isGetNameAddressOnly()) {
- // if GetNameAddressOnly is set, we can clear this flag,
- // so the service won't be unbind
- // after name and address are saved
- mConnection.setGetNameAddressOnly(false);
- //Register callback object
- try {
- mBluetooth.registerCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothCallback",re);
- }
- //Inform BluetoothAdapter instances that service is up
- sendBluetoothServiceUpCallback();
- }
-
//Enable bluetooth
try {
if (!mQuietEnable) {
@@ -1594,9 +1438,7 @@
private void handleDisable() {
synchronized(mConnection) {
- // don't need to disable if GetNameAddressOnly is set,
- // service will be unbinded after Name and Address are saved
- if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
+ if (mBluetooth != null) {
if (DBG) Log.d(TAG,"Sending off request.");
try {
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 73e8c52..019d03d 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -31,14 +31,15 @@
@Override
public void onStart() {
- Log.d(TAG, "onStart: publishing BluetoothManagerService");
- publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
+ publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
+ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ Log.d(TAG, "onBootPhase: PHASE_ACTIVITY_MANAGER_READY");
mBluetoothManagerService.handleOnBootPhase();
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 927b995..f5ed83e 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -48,6 +48,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
@@ -106,6 +107,8 @@
private static final boolean COMPRESS_TIME = false;
+ private static final int EVENT_BUFFER_SIZE = 40;
+
private static final String ACTION_STEP_IDLE_STATE =
"com.android.server.device_idle.STEP_IDLE_STATE";
@@ -195,6 +198,14 @@
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
private long mNextLightAlarmTime;
+ private long mCurIdleBudget;
+ private long mMaintenanceStartTime;
+
+ private int mActiveIdleOpCount;
+ private IBinder mDownloadServiceActive;
+ private boolean mSyncActive;
+ private boolean mJobsActive;
+ private boolean mAlarmsActive;
public final AtomicFile mConfigFile;
@@ -267,6 +278,25 @@
*/
private int[] mTempWhitelistAppIdArray = new int[0];
+ private static final int EVENT_NULL = 0;
+ private static final int EVENT_NORMAL = 1;
+ private static final int EVENT_LIGHT_IDLE = 2;
+ private static final int EVENT_LIGHT_MAINTENANCE = 3;
+ private static final int EVENT_FULL_IDLE = 4;
+ private static final int EVENT_FULL_MAINTENANCE = 5;
+
+ private int[] mEventCmds = new int[EVENT_BUFFER_SIZE];
+ private long[] mEventTimes = new long[EVENT_BUFFER_SIZE];
+
+ private void addEvent(int cmd) {
+ if (mEventCmds[0] != cmd) {
+ System.arraycopy(mEventCmds, 0, mEventCmds, 1, EVENT_BUFFER_SIZE - 1);
+ System.arraycopy(mEventTimes, 0, mEventTimes, 1, EVENT_BUFFER_SIZE - 1);
+ mEventCmds[0] = cmd;
+ mEventTimes[0] = SystemClock.elapsedRealtime();
+ }
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
@@ -282,16 +312,22 @@
}
} else if (ACTION_STEP_LIGHT_IDLE_STATE.equals(intent.getAction())) {
synchronized (DeviceIdleController.this) {
- stepLightIdleStateLocked();
+ stepLightIdleStateLocked("s:alarm");
}
} else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {
synchronized (DeviceIdleController.this) {
- stepIdleStateLocked();
+ stepIdleStateLocked("s:alarm");
}
}
}
};
+ private final BroadcastReceiver mIdleStartedDoneReceiver = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ decActiveIdleOps();
+ }
+ };
+
private final DisplayManager.DisplayListener mDisplayListener
= new DisplayManager.DisplayListener() {
@Override public void onDisplayAdded(int displayId) {
@@ -411,7 +447,10 @@
private final class Constants extends ContentObserver {
// Key names stored in the settings value.
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
- private static final String KEY_LIGHT_IDLE_PENDING_TIMEOUT = "light_idle_pending_to";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
+ = "light_idle_maintenance_min_budget";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
+ = "light_idle_maintenance_max_budget";
private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
private static final String KEY_SENSING_TIMEOUT = "sensing_to";
private static final String KEY_LOCATING_TIMEOUT = "locating_to";
@@ -441,12 +480,24 @@
public long LIGHT_IDLE_TIMEOUT;
/**
- * This is the initial time, after light idle idle, that we will will sit in the
- * LIGHT_IDLE_MAINTENANCE period for the system to run normally before returning to idle.
+ * This is the minimum amount of time we want to make available for maintenance mode
+ * when lightly idling. That is, we will always have at least this amount of time
+ * available maintenance before timing out and cutting off maintenance mode.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
- * @see #KEY_LIGHT_IDLE_PENDING_TIMEOUT
+ * @see #KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
*/
- public long LIGHT_IDLE_PENDING_TIMEOUT;
+ public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+
+ /**
+ * This is the maximum amount of time we want to make available for maintenance mode
+ * when lightly idling. That is, if the system isn't using up its minimum maintenance
+ * budget and this time is being added to the budget reserve, this is the maximum
+ * reserve size we will allow to grow and thus the maximum amount of time we will
+ * allow for the maintenance window.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
+ */
+ public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
/**
* This is the time, after becoming inactive, at which we start looking at the
@@ -606,8 +657,12 @@
LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT,
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);
- LIGHT_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_PENDING_TIMEOUT,
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
!COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L);
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+ !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
@@ -649,8 +704,12 @@
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LIGHT_IDLE_PENDING_TIMEOUT); pw.print("=");
- TimeUtils.formatDuration(LIGHT_IDLE_PENDING_TIMEOUT, pw);
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_MAINTENANCE_MIN_BUDGET, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_MAINTENANCE_MAX_BUDGET, pw);
pw.println();
pw.print(" "); pw.print(KEY_INACTIVE_TIMEOUT); pw.print("=");
@@ -733,7 +792,7 @@
// If we are currently sensing, it is time to move to locating.
synchronized (this) {
mNotMoving = true;
- stepIdleStateLocked();
+ stepIdleStateLocked("s:stationary");
}
} else if (mState == STATE_LOCATING) {
// If we are currently locating, note that we are not moving and step
@@ -741,7 +800,7 @@
synchronized (this) {
mNotMoving = true;
if (mLocated) {
- stepIdleStateLocked();
+ stepIdleStateLocked("s:stationary");
}
}
}
@@ -804,11 +863,18 @@
} catch (RemoteException e) {
}
if (fullChanged) {
- getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ incActiveIdleOps();
+ getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,
+ null, mIdleStartedDoneReceiver, null, 0, null, null);
}
if (lightChanged) {
- getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
+ incActiveIdleOps();
+ getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
+ null, mIdleStartedDoneReceiver, null, 0, null, null);
}
+ // Always start with one active op for the message being sent here.
+ // Now we we done!
+ decActiveIdleOps();
EventLogTags.writeDeviceIdleOffComplete();
} break;
case MSG_REPORT_ACTIVE: {
@@ -913,11 +979,23 @@
}
@Override public void exitIdle(String reason) {
- getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
null);
exitIdleInternal(reason);
}
+ @Override public void downloadServiceActive(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(
+ "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS", null);
+ DeviceIdleController.this.downloadServiceActive(token);
+ }
+
+ @Override public void downloadServiceInactive() {
+ getContext().enforceCallingOrSelfPermission(
+ "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS", null);
+ DeviceIdleController.this.downloadServiceInactive();
+ }
+
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DeviceIdleController.this.dump(fd, pw, args);
}
@@ -937,6 +1015,19 @@
public void setNetworkPolicyTempWhitelistCallback(Runnable callback) {
setNetworkPolicyTempWhitelistCallbackInternal(callback);
}
+
+ public void setSyncActive(boolean active) {
+ DeviceIdleController.this.setSyncActive(active);
+ }
+
+ public void setJobsActive(boolean active) {
+ DeviceIdleController.this.setJobsActive(active);
+ }
+
+ // Up-call from alarm manager.
+ public void setAlarmsActive(boolean active) {
+ DeviceIdleController.this.setAlarmsActive(active);
+ }
}
public DeviceIdleController(Context context) {
@@ -1390,8 +1481,11 @@
mState = STATE_ACTIVE;
mLightState = LIGHT_STATE_ACTIVE;
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
+ mCurIdleBudget = 0;
+ mMaintenanceStartTime = 0;
resetIdleManagementLocked();
resetLightIdleManagementLocked();
+ addEvent(EVENT_NORMAL);
}
}
@@ -1439,7 +1533,7 @@
}
}
- void stepLightIdleStateLocked() {
+ void stepLightIdleStateLocked(String reason) {
if (mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in full device idle mode, then
// there is nothing left to do for light mode.
@@ -1451,26 +1545,49 @@
switch (mLightState) {
case LIGHT_STATE_INACTIVE:
+ mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ mMaintenanceStartTime = 0;
case LIGHT_STATE_IDLE_MAINTENANCE:
+ if (mMaintenanceStartTime != 0) {
+ long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
+ if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ // We didn't use up all of our minimum budget; add this to the reserve.
+ mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);
+ } else {
+ // We used more than our minimum budget; this comes out of the reserve.
+ mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+ }
+ }
+ mMaintenanceStartTime = 0;
scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT);
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
mLightState = LIGHT_STATE_IDLE;
- EventLogTags.writeDeviceIdleLight(mLightState, "step");
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_IDLE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
break;
case LIGHT_STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_PENDING_TIMEOUT);
+ mActiveIdleOpCount = 1;
+ mMaintenanceStartTime = SystemClock.elapsedRealtime();
+ if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
+ mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+ }
+ mMaintenanceStartTime = SystemClock.elapsedRealtime();
+ scheduleLightAlarmLocked(mCurIdleBudget);
if (DEBUG) Slog.d(TAG,
"Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
- EventLogTags.writeDeviceIdleLight(mLightState, "step");
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_MAINTENANCE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
break;
}
}
- void stepIdleStateLocked() {
+ void stepIdleStateLocked(String reason) {
if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
EventLogTags.writeDeviceIdleStep();
@@ -1494,12 +1611,12 @@
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
mState = STATE_IDLE_PENDING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
- EventLogTags.writeDeviceIdle(mState, "step");
+ EventLogTags.writeDeviceIdle(mState, reason);
break;
case STATE_IDLE_PENDING:
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
- EventLogTags.writeDeviceIdle(mState, "step");
+ EventLogTags.writeDeviceIdle(mState, reason);
scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
cancelLocatingLocked();
mAnyMotionDetector.checkForAnyMotion();
@@ -1511,7 +1628,7 @@
case STATE_SENSING:
mState = STATE_LOCATING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
- EventLogTags.writeDeviceIdle(mState, "step");
+ EventLogTags.writeDeviceIdle(mState, reason);
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
if (mLocationManager != null
&& mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
@@ -1553,23 +1670,103 @@
mLightState = LIGHT_STATE_OVERRIDE;
cancelLightAlarmLocked();
}
- EventLogTags.writeDeviceIdle(mState, "step");
+ EventLogTags.writeDeviceIdle(mState, reason);
+ addEvent(EVENT_FULL_IDLE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
break;
case STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
+ mActiveIdleOpCount = 1;
scheduleAlarmLocked(mNextIdlePendingDelay, false);
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
"Next alarm in " + mNextIdlePendingDelay + " ms.");
mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
mState = STATE_IDLE_MAINTENANCE;
- EventLogTags.writeDeviceIdle(mState, "step");
+ EventLogTags.writeDeviceIdle(mState, reason);
+ addEvent(EVENT_FULL_MAINTENANCE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
break;
}
}
+ void incActiveIdleOps() {
+ synchronized (this) {
+ mActiveIdleOpCount++;
+ }
+ }
+
+ void decActiveIdleOps() {
+ synchronized (this) {
+ mActiveIdleOpCount--;
+ if (mActiveIdleOpCount <= 0) {
+ exitMaintenanceEarlyIfNeededLocked();
+ }
+ }
+ }
+
+ void downloadServiceActive(IBinder token) {
+ synchronized (this) {
+ mDownloadServiceActive = token;
+ try {
+ token.linkToDeath(new IBinder.DeathRecipient() {
+ @Override public void binderDied() {
+ downloadServiceInactive();
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ mDownloadServiceActive = null;
+ }
+ }
+ }
+
+ void downloadServiceInactive() {
+ synchronized (this) {
+ mDownloadServiceActive = null;
+ exitMaintenanceEarlyIfNeededLocked();
+ }
+ }
+
+ void setSyncActive(boolean active) {
+ synchronized (this) {
+ mSyncActive = active;
+ if (!active) {
+ exitMaintenanceEarlyIfNeededLocked();
+ }
+ }
+ }
+
+ void setJobsActive(boolean active) {
+ synchronized (this) {
+ mJobsActive = active;
+ if (!active) {
+ exitMaintenanceEarlyIfNeededLocked();
+ }
+ }
+ }
+
+ void setAlarmsActive(boolean active) {
+ synchronized (this) {
+ mAlarmsActive = active;
+ if (!active) {
+ exitMaintenanceEarlyIfNeededLocked();
+ }
+ }
+ }
+
+ void exitMaintenanceEarlyIfNeededLocked() {
+ if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) {
+ if (mActiveIdleOpCount <= 0 && mDownloadServiceActive == null
+ && !mSyncActive && !mJobsActive && !mAlarmsActive) {
+ if (mState == STATE_IDLE_MAINTENANCE) {
+ stepIdleStateLocked("s:early");
+ } else {
+ stepLightIdleStateLocked("s:early");
+ }
+ }
+ }
+ }
+
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
// The motion sensor will have been disabled at this point
@@ -1585,7 +1782,10 @@
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
mInactiveTimeout = timeout;
+ mCurIdleBudget = 0;
+ mMaintenanceStartTime = 0;
EventLogTags.writeDeviceIdle(mState, type);
+ addEvent(EVENT_NORMAL);
becomeInactive = true;
}
if (mLightState == LIGHT_STATE_OVERRIDE) {
@@ -1612,7 +1812,7 @@
}
mLocated = true;
if (mNotMoving) {
- stepIdleStateLocked();
+ stepIdleStateLocked("s:location");
}
}
@@ -1628,7 +1828,7 @@
}
mLocated = true;
if (mNotMoving) {
- stepIdleStateLocked();
+ stepIdleStateLocked("s:gps");
}
}
@@ -1892,6 +2092,8 @@
pw.println(" Print this help text.");
pw.println(" step");
pw.println(" Immediately step to next state, without waiting for alarm.");
+ pw.println(" light-step");
+ pw.println(" Immediately step to next light idle state, without waiting for alarm.");
pw.println(" force-idle");
pw.println(" Force directly into idle mode, regardless of other device state.");
pw.println(" Use \"step\" to get out.");
@@ -1933,7 +2135,7 @@
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
- stepIdleStateLocked();
+ stepIdleStateLocked("s:shell");
pw.print("Stepped to: ");
pw.println(stateToString(mState));
} finally {
@@ -1947,7 +2149,7 @@
long token = Binder.clearCallingIdentity();
try {
exitForceIdleLocked();
- stepLightIdleStateLocked();
+ stepLightIdleStateLocked("s:shell");
pw.print("Stepped to: "); pw.println(lightStateToString(mLightState));
} finally {
Binder.restoreCallingIdentity(token);
@@ -1967,7 +2169,7 @@
becomeInactiveIfAppropriateLocked();
int curState = mState;
while (curState != STATE_IDLE) {
- stepIdleStateLocked();
+ stepIdleStateLocked("s:shell");
if (curState == mState) {
pw.print("Unable to go idle; stopped at ");
pw.println(stateToString(mState));
@@ -2138,6 +2340,31 @@
synchronized (this) {
mConstants.dump(pw);
+ if (mEventCmds[0] != EVENT_NULL) {
+ pw.println(" Idling history:");
+ long now = SystemClock.elapsedRealtime();
+ for (int i=EVENT_BUFFER_SIZE-1; i>=0; i--) {
+ int cmd = mEventCmds[i];
+ if (cmd == EVENT_NULL) {
+ continue;
+ }
+ String label;
+ switch (mEventCmds[i]) {
+ case EVENT_NORMAL: label = " normal"; break;
+ case EVENT_LIGHT_IDLE: label = " light-idle"; break;
+ case EVENT_LIGHT_MAINTENANCE: label = "light-maint"; break;
+ case EVENT_FULL_IDLE: label = " full-idle"; break;
+ case EVENT_FULL_MAINTENANCE: label = " full-maint"; break;
+ default: label = " ??"; break;
+ }
+ pw.print(" ");
+ pw.print(label);
+ pw.print(": ");
+ TimeUtils.formatDuration(mEventTimes[i], now, pw);;
+ pw.println();
+ }
+ }
+
int size = mPowerSaveWhitelistAppsExceptIdle.size();
if (size > 0) {
pw.println(" Whitelist (except idle) system apps:");
@@ -2226,6 +2453,9 @@
pw.println(lightStateToString(mLightState));
pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw);
pw.println();
+ if (mActiveIdleOpCount != 0) {
+ pw.print(" mActiveIdleOpCount="); pw.println(mActiveIdleOpCount);
+ }
if (mNextAlarmTime != 0) {
pw.print(" mNextAlarmTime=");
TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
@@ -2246,6 +2476,28 @@
TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw);
pw.println();
}
+ if (mCurIdleBudget != 0) {
+ pw.print(" mCurIdleBudget=");
+ TimeUtils.formatDuration(mCurIdleBudget, pw);
+ pw.println();
+ }
+ if (mMaintenanceStartTime != 0) {
+ pw.print(" mMaintenanceStartTime=");
+ TimeUtils.formatDuration(mMaintenanceStartTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ }
+ if (mSyncActive) {
+ pw.print(" mSyncActive="); pw.println(mSyncActive);
+ }
+ if (mJobsActive) {
+ pw.print(" mJobsActive="); pw.println(mJobsActive);
+ }
+ if (mAlarmsActive) {
+ pw.print(" mAlarmsActive="); pw.println(mAlarmsActive);
+ }
+ if (mDownloadServiceActive != null) {
+ pw.print(" mDownloadServiceActive="); pw.println(mDownloadServiceActive);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index ab1d775..5e4f2b2 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -30,6 +30,7 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InputBindResult;
+import com.android.internal.view.InputMethodClient;
import com.android.server.statusbar.StatusBarManagerService;
import org.xmlpull.v1.XmlPullParser;
@@ -160,8 +161,8 @@
static final int MSG_START_INPUT = 2000;
static final int MSG_RESTART_INPUT = 2010;
- static final int MSG_UNBIND_METHOD = 3000;
- static final int MSG_BIND_METHOD = 3010;
+ static final int MSG_UNBIND_CLIENT = 3000;
+ static final int MSG_BIND_CLIENT = 3010;
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
@@ -308,11 +309,18 @@
ClientState mCurClient;
/**
- * The last window token that gained focus.
+ * The last window token that we confirmed to be focused. This is always updated upon reports
+ * from the input method client. If the window state is already changed before the report is
+ * handled, this field just keeps the last value.
*/
IBinder mCurFocusedWindow;
/**
+ * The client by which {@link #mCurFocusedWindow} was reported. Used only for debugging.
+ */
+ ClientState mCurFocusedWindowClient;
+
+ /**
* The input context last provided by the current client.
*/
IInputContext mCurInputContext;
@@ -935,7 +943,7 @@
|| (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
if (!updateOnlyWhenLocaleChanged) {
hideCurrentInputLocked(0, null);
- unbindCurrentMethodLocked(true, false);
+ resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_RESET_IME);
}
if (DEBUG) {
Slog.i(TAG, "Locale has been changed to " + newLocale);
@@ -1195,6 +1203,12 @@
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
clearClientSessionLocked(cs);
+ if (mCurClient == cs) {
+ mCurClient = null;
+ }
+ if (mCurFocusedWindowClient == cs) {
+ mCurFocusedWindowClient = null;
+ }
}
}
}
@@ -1208,7 +1222,8 @@
}
}
- void unbindCurrentClientLocked() {
+ void unbindCurrentClientLocked(
+ /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client = "
+ mCurClient.client.asBinder());
@@ -1222,8 +1237,8 @@
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_SET_ACTIVE, 0, mCurClient));
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
+ MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
mCurClient.sessionRequested = false;
mCurClient = null;
@@ -1324,7 +1339,7 @@
mCurClientInKeyguard = isKeyguardLocked();
// If the client is changing, we need to switch over to the new
// one.
- unbindCurrentClientLocked();
+ unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
if (DEBUG) Slog.v(TAG, "switching to client: client = "
+ cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
@@ -1395,7 +1410,7 @@
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
- unbindCurrentMethodLocked(false, true);
+ unbindCurrentMethodLocked(true);
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
@@ -1453,7 +1468,7 @@
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
- unbindCurrentMethodLocked(false, false);
+ unbindCurrentMethodLocked(false);
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
@@ -1479,7 +1494,7 @@
InputBindResult res = attachNewInputLocked(true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
- MSG_BIND_METHOD, mCurClient.client, res));
+ MSG_BIND_CLIENT, mCurClient.client, res));
}
return;
}
@@ -1490,11 +1505,7 @@
channel.dispose();
}
- void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) {
- if (resetCurrentMethodAndClient) {
- mCurMethodId = null;
- }
-
+ void unbindCurrentMethodLocked(boolean savePosition) {
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
@@ -1520,10 +1531,13 @@
mCurId = null;
clearCurMethodLocked();
+ }
- if (resetCurrentMethodAndClient) {
- unbindCurrentClientLocked();
- }
+ void resetCurrentMethodAndClient(
+ /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+ mCurMethodId = null;
+ unbindCurrentMethodLocked(false);
+ unbindCurrentClientLocked(unbindClientReason);
}
void requestClientSessionLocked(ClientState cs) {
@@ -1590,8 +1604,9 @@
mShowRequested = mInputShown;
mInputShown = false;
if (mCurClient != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
+ MSG_UNBIND_CLIENT, InputMethodClient.UNBIND_REASON_DISCONNECT_IME,
+ mCurSeq, mCurClient.client));
}
}
}
@@ -1876,12 +1891,12 @@
setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
- unbindCurrentMethodLocked(true, false);
+ resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
}
mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
- unbindCurrentMethodLocked(true, false);
+ resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
}
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
@@ -1967,7 +1982,7 @@
intent.putExtra("input_method_id", id);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
- unbindCurrentClientLocked();
+ unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2189,6 +2204,7 @@
return null;
}
mCurFocusedWindow = windowToken;
+ mCurFocusedWindowClient = cs;
// Should we auto-show the IME even if the caller has not
// specified what should be done with it?
@@ -2771,14 +2787,14 @@
// ---------------------------------------------------------
- case MSG_UNBIND_METHOD:
+ case MSG_UNBIND_CLIENT:
try {
- ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1);
+ ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
} catch (RemoteException e) {
// There is nothing interesting about the last client dying.
}
return true;
- case MSG_BIND_METHOD: {
+ case MSG_BIND_CLIENT: {
args = (SomeArgs)msg.obj;
IInputMethodClient client = (IInputMethodClient)args.arg1;
InputBindResult res = (InputBindResult)args.arg2;
@@ -3700,6 +3716,7 @@
IInputMethod method;
ClientState client;
+ ClientState focusedWindowClient;
final Printer p = new PrintWriterPrinter(pw);
@@ -3724,6 +3741,8 @@
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
p.println(" mCurFocusedWindow=" + mCurFocusedWindow);
+ focusedWindowClient = mCurFocusedWindowClient;
+ p.println(" mCurFocusedWindowClient=" + focusedWindowClient);
p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
+ " mBoundToMethod=" + mBoundToMethod);
p.println(" mCurToken=" + mCurToken);
@@ -3755,6 +3774,20 @@
p.println("No input method client.");
}
+ if (focusedWindowClient != null && client != focusedWindowClient) {
+ p.println(" ");
+ p.println("Warning: Current input method client doesn't match the last focused. "
+ + "window.");
+ p.println("Dumping input method client in the last focused window just in case.");
+ p.println(" ");
+ pw.flush();
+ try {
+ focusedWindowClient.client.asBinder().dump(fd, args);
+ } catch (RemoteException e) {
+ p.println("Input method client in focused window dead: " + e);
+ }
+ }
+
p.println(" ");
if (method != null) {
pw.flush();
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 3359060..43d10c7 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -226,7 +226,7 @@
final int N = a.length;
boolean printedHeader = false;
F filter;
- if (collapseDuplicates) {
+ if (collapseDuplicates && !printFilter) {
found.clear();
for (int i=0; i<N && (filter=a[i]) != null; i++) {
if (packageName != null && !isPackageForFilter(packageName, filter)) {
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 6acec6b..eb49a78 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -389,7 +389,7 @@
private int getUserParentOrSelfId(int userId) {
// Device supports per user encryption, so lock is applied to the given user.
- if (mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ if (StorageManager.isFileBasedEncryptionEnabled()) {
return userId;
}
// Device uses Block Based Encryption, and the parent user's lock is used for the whole
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 6e4f238..a32bb2f 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -56,6 +56,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -96,6 +97,7 @@
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
@@ -119,6 +121,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -284,6 +287,8 @@
@GuardedBy("mLock")
private int[] mStartedUsers = EmptyArray.INT;
+ @GuardedBy("mLock")
+ private int[] mUnlockedUsers = EmptyArray.INT;
/** Map from disk ID to disk */
@GuardedBy("mLock")
@@ -402,6 +407,17 @@
}
}
+ private static String escapeNull(String arg) {
+ if (TextUtils.isEmpty(arg)) {
+ return "!";
+ } else {
+ if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
+ throw new IllegalArgumentException(arg);
+ }
+ return arg;
+ }
+ }
+
/** List of crypto types.
* These must match CRYPT_TYPE_XXX in cryptfs.h AND their
* corresponding commands in CommandListener.cpp */
@@ -1888,13 +1904,18 @@
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
- synchronized (mLock) {
- if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
- mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
- }
+ if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
+ final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0;
+ SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe));
+ }
- writeSettingsLocked();
- mHandler.obtainMessage(H_RESET).sendToTarget();
+ if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
+ synchronized (mLock) {
+ mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
+
+ writeSettingsLocked();
+ mHandler.obtainMessage(H_RESET).sendToTarget();
+ }
}
}
@@ -2654,65 +2675,97 @@
}
@Override
- public void createNewUserDir(int userHandle, String path) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only SYSTEM_UID can create user directories");
- }
-
+ public void createUserKey(int userId, int serialNumber) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
- if (DEBUG_EVENTS) {
- Slog.i(TAG, "Creating new user dir");
- }
-
try {
- NativeDaemonEvent event = mCryptConnector.execute(
- "cryptfs", "createnewuserdir", userHandle, path);
- if (!"0".equals(event.getMessage())) {
- String error = "createnewuserdir sent unexpected message: "
- + event.getMessage();
- Slog.e(TAG, error);
- // ext4enc:TODO is this the right exception?
- throw new RuntimeException(error);
- }
+ mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber);
} catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "createnewuserdir threw exception", e);
- throw new RuntimeException("createnewuserdir threw exception", e);
- }
- }
-
- // ext4enc:TODO duplication between this and createNewUserDir is nasty
- @Override
- public void deleteUserKey(int userHandle) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only SYSTEM_UID can delete user keys");
- }
-
- waitForReady();
-
- if (DEBUG_EVENTS) {
- Slog.i(TAG, "Deleting user key");
- }
-
- try {
- NativeDaemonEvent event = mCryptConnector.execute(
- "cryptfs", "deleteuserkey", userHandle);
- if (!"0".equals(event.getMessage())) {
- String error = "deleteuserkey sent unexpected message: "
- + event.getMessage();
- Slog.e(TAG, error);
- // ext4enc:TODO is this the right exception?
- throw new RuntimeException(error);
- }
- } catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "deleteuserkey threw exception", e);
- throw new RuntimeException("deleteuserkey threw exception", e);
+ throw e.rethrowAsParcelableException();
}
}
@Override
- public boolean isPerUserEncryptionEnabled() {
- return "file".equals(SystemProperties.get("ro.crypto.type", "none"));
+ public void destroyUserKey(int userId) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "destroy_user_key", userId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ final String encodedToken;
+ if (ArrayUtils.isEmpty(token)) {
+ encodedToken = "!";
+ } else {
+ encodedToken = HexDump.toHexString(token);
+ }
+
+ try {
+ mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
+ new SensitiveArg(encodedToken));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+
+ synchronized (mLock) {
+ mUnlockedUsers = ArrayUtils.appendInt(mUnlockedUsers, userId);
+ }
+ }
+
+ @Override
+ public void lockUserKey(int userId) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "lock_user_key", userId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+
+ synchronized (mLock) {
+ mUnlockedUsers = ArrayUtils.removeInt(mUnlockedUsers, userId);
+ }
+ }
+
+ @Override
+ public boolean isUserKeyUnlocked(int userId) {
+ if (StorageManager.isFileBasedEncryptionEnabled()) {
+ synchronized (mLock) {
+ return ArrayUtils.contains(mUnlockedUsers, userId);
+ }
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
+ userId, serialNumber);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
+ // TODO: Invoke vold to mount app fuse.
+ throw new UnsupportedOperationException();
}
@Override
@@ -3449,6 +3502,9 @@
pw.println();
pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
pw.println("Force adoptable: " + mForceAdoptable);
+ pw.println();
+ pw.println("Started users: " + Arrays.toString(mStartedUsers));
+ pw.println("Unlocked users: " + Arrays.toString(mUnlockedUsers));
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c228422..9eb66dd 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -88,6 +88,7 @@
private SettingsObserver mSettingObserver;
native static boolean vibratorExists();
+ native static void vibratorInit();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
@@ -195,6 +196,7 @@
}
VibratorService(Context context) {
+ vibratorInit();
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
vibratorOff();
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 9ac4ba3..93eaf0e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -88,6 +88,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
@@ -114,6 +115,7 @@
public class AccountManagerService
extends IAccountManager.Stub
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
+
private static final String TAG = "AccountManagerService";
private static final String DATABASE_NAME = "accounts.db";
@@ -2283,6 +2285,196 @@
}
}
+ @Override
+ public void startAddAccountSession(
+ final IAccountManagerResponse response,
+ final String accountType,
+ final String authTokenType,
+ final String[] requiredFeatures,
+ final boolean expectActivityLaunch,
+ final Bundle optionsIn) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "startAddAccountSession: accountType " + accountType
+ + ", response " + response
+ + ", authTokenType " + authTokenType
+ + ", requiredFeatures " + stringArrayToString(requiredFeatures)
+ + ", expectActivityLaunch " + expectActivityLaunch
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (response == null) {
+ throw new IllegalArgumentException("response is null");
+ }
+ if (accountType == null) {
+ throw new IllegalArgumentException("accountType is null");
+ }
+
+ int userId = Binder.getCallingUserHandle().getIdentifier();
+ if (!canUserModifyAccounts(userId)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+ "User is not allowed to add an account!");
+ } catch (RemoteException re) {
+ }
+ showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
+ return;
+ }
+ if (!canUserModifyAccountsForType(userId, accountType)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+ "User cannot modify accounts of this type (policy).");
+ } catch (RemoteException re) {
+ }
+ showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+ userId);
+ return;
+ }
+
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
+ options.putInt(AccountManager.KEY_CALLER_UID, uid);
+ options.putInt(AccountManager.KEY_CALLER_PID, pid);
+
+ int usrId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(usrId);
+ logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
+ TABLE_ACCOUNTS, uid);
+ new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
+ null /* accountName */, false /* authDetailsRequired */,
+ true /* updateLastAuthenticationTime */) {
+ @Override
+ public void run() throws RemoteException {
+ mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
+ requiredFeatures, options);
+ }
+
+ @Override
+ protected String toDebugString(long now) {
+ String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
+ return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
+ + accountType + ", requiredFeatures "
+ + (requiredFeatures != null ? requiredFeaturesStr : null);
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
+ private abstract class StartAccountSession extends Session {
+
+ public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
+ String accountType, boolean expectActivityLaunch, String accountName,
+ boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
+ super(accounts, response, accountType, expectActivityLaunch,
+ true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
+ updateLastAuthenticationTime);
+ }
+
+ @Override
+ public void onResult(Bundle result) {
+ mNumResults++;
+ Intent intent = null;
+
+ if (result != null
+ && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ /*
+ * The Authenticator API allows third party authenticators to
+ * supply arbitrary intents to other apps that they can run,
+ * this can be very bad when those apps are in the system like
+ * the System Settings.
+ */
+ int authenticatorUid = Binder.getCallingUid();
+ long bid = Binder.clearCallingIdentity();
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+ int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
+ if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
+ targetUid)) {
+ throw new SecurityException("Activity to be started with KEY_INTENT must "
+ + "share Authenticator's signatures");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(bid);
+ }
+ }
+
+ IAccountManagerResponse response;
+ if (mExpectActivityLaunch && result != null
+ && result.containsKey(AccountManager.KEY_INTENT)) {
+ response = mResponse;
+ } else {
+ response = getResponseAndClose();
+ }
+ if (response == null) {
+ return;
+ }
+ if (result == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
+ + response);
+ }
+ sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "null bundle returned");
+ return;
+ }
+
+ if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
+ // All AccountManager error codes are greater
+ // than 0
+ sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
+ result.getString(AccountManager.KEY_ERROR_MESSAGE));
+ return;
+ }
+
+ // Strip auth token from result.
+ result.remove(AccountManager.KEY_AUTHTOKEN);
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ getClass().getSimpleName() + " calling onResult() on response " + response);
+ }
+
+ // Get the session bundle created by authenticator. The
+ // bundle contains data necessary for finishing the session
+ // later. The session bundle will be encrypted here and
+ // decrypted later when trying to finish the session.
+ Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+ if (sessionBundle != null) {
+ String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
+ if (TextUtils.isEmpty(accountType)
+ && !mAccountType.equalsIgnoreCase(mAccountType)) {
+ Log.w(TAG, "Account type in session bundle doesn't match request.");
+ }
+ // Add accountType info to session bundle. This will
+ // override any value set by authenticator.
+ sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
+
+ // Encrypt session bundle before returning to caller.
+ try {
+ CryptoHelper cryptoHelper = CryptoHelper.getInstance();
+ Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
+ } catch (GeneralSecurityException e) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.v(TAG, "Failed to encrypt session bundle!", e);
+ }
+ sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "failed to encrypt session bundle");
+ return;
+ }
+ }
+
+ sendResponse(response, result);
+ }
+ }
+
private void showCantAddAccount(int errorCode, int userId) {
Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
@@ -2382,6 +2574,60 @@
}
@Override
+ public void startUpdateCredentialsSession(
+ IAccountManagerResponse response,
+ final Account account,
+ final String authTokenType,
+ final boolean expectActivityLaunch,
+ final Bundle loginOptions) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "startUpdateCredentialsSession: " + account + ", response " + response
+ + ", authTokenType " + authTokenType + ", expectActivityLaunch "
+ + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (response == null) {
+ throw new IllegalArgumentException("response is null");
+ }
+ if (account == null) {
+ throw new IllegalArgumentException("account is null");
+ }
+ int userId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(userId);
+ new StartAccountSession(
+ accounts,
+ response,
+ account.type,
+ expectActivityLaunch,
+ account.name,
+ false /* authDetailsRequired */,
+ true /* updateLastCredentialTime */) {
+ @Override
+ public void run() throws RemoteException {
+ mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
+ loginOptions);
+ }
+
+ @Override
+ protected String toDebugString(long now) {
+ if (loginOptions != null)
+ loginOptions.keySet();
+ return super.toDebugString(now)
+ + ", startUpdateCredentialsSession"
+ + ", " + account
+ + ", authTokenType " + authTokenType
+ + ", loginOptions " + loginOptions;
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
public void editProperties(IAccountManagerResponse response, final String accountType,
final boolean expectActivityLaunch) {
final int callingUid = Binder.getCallingUid();
@@ -3336,6 +3582,11 @@
private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
+ // TODO: This action doesn't add account to accountdb. Account is only
+ // added in finishAddAccount or finishAddAccountAsUser which may be in
+ // a different user profile.
+ private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
+
private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static void createDebugTable(SQLiteDatabase db) {
@@ -4300,4 +4551,29 @@
return mContext;
}
}
+
+ private void sendResponse(IAccountManagerResponse response, Bundle result) {
+ try {
+ response.onResult(result);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+
+ private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
+ String errorMessage) {
+ try {
+ response.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/accounts/CryptoHelper.java b/services/core/java/com/android/server/accounts/CryptoHelper.java
new file mode 100644
index 0000000..2b59b74
--- /dev/null
+++ b/services/core/java/com/android/server/accounts/CryptoHelper.java
@@ -0,0 +1,140 @@
+package com.android.server.accounts;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
+ * key for {@link AccountManagerService}.
+ */
+/* default */ class CryptoHelper {
+ private static final String TAG = "Account";
+
+ private static final String KEY_CIPHER = "cipher";
+ private static final String KEY_MAC = "mac";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
+ private static final String MAC_ALGORITHM = "HMACSHA256";
+ private static final int IV_LENGTH = 16;
+
+ private static CryptoHelper sInstance;
+ // Keys used for encrypting and decrypting data returned in a Bundle.
+ private final SecretKeySpec mCipherKeySpec;
+ private final SecretKeySpec mMacKeySpec;
+ private final IvParameterSpec mIv;
+
+ /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
+ if (sInstance == null) {
+ sInstance = new CryptoHelper();
+ }
+ return sInstance;
+ }
+
+ private CryptoHelper() throws NoSuchAlgorithmException {
+ KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
+ SecretKey skey = kgen.generateKey();
+ mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
+
+ kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
+ skey = kgen.generateKey();
+ mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
+
+ // Create random iv
+ byte[] iv = new byte[IV_LENGTH];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(iv);
+ mIv = new IvParameterSpec(iv);
+ }
+
+ @NonNull
+ /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
+ Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
+ Parcel parcel = Parcel.obtain();
+ bundle.writeToParcel(parcel, 0);
+ byte[] bytes = parcel.marshall();
+ parcel.recycle();
+
+ Bundle encryptedBundle = new Bundle();
+
+ byte[] cipher = encrypt(bytes);
+ byte[] mac = createMac(cipher);
+
+ encryptedBundle.putByteArray(KEY_CIPHER, cipher);
+ encryptedBundle.putByteArray(KEY_MAC, mac);
+
+ return encryptedBundle;
+ }
+
+ @Nullable
+ /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
+ Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
+ byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
+ byte[] macArray = bundle.getByteArray(KEY_MAC);
+
+ if (!verifyMac(cipherArray, macArray)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Escrow mac mismatched!");
+ }
+ return null;
+ }
+
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
+ byte[] decryptedBytes = cipher.doFinal(cipherArray);
+
+ Parcel decryptedParcel = Parcel.obtain();
+ decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
+ decryptedParcel.setDataPosition(0);
+ Bundle decryptedBundle = new Bundle();
+ decryptedBundle.readFromParcel(decryptedParcel);
+ decryptedParcel.recycle();
+ return decryptedBundle;
+ }
+
+ private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
+ throws GeneralSecurityException {
+
+ if (cipherArray == null || cipherArray.length == 0 || macArray == null
+ || macArray.length == 0) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Cipher or MAC is empty!");
+ }
+ return false;
+ }
+ Mac mac = Mac.getInstance(MAC_ALGORITHM);
+ mac.init(mMacKeySpec);
+ mac.update(cipherArray);
+ return Arrays.equals(macArray, mac.doFinal());
+ }
+
+ @NonNull
+ private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
+ return cipher.doFinal(data);
+ }
+
+ @NonNull
+ private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
+ Mac mac = Mac.getInstance(MAC_ALGORITHM);
+ mac.init(mMacKeySpec);
+ return mac.doFinal(cipher);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2820216..17b3d2a 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -168,13 +168,10 @@
*/
class ServiceMap extends Handler {
final int mUserId;
- final ArrayMap<ComponentName, ServiceRecord> mServicesByName
- = new ArrayMap<ComponentName, ServiceRecord>();
- final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent
- = new ArrayMap<Intent.FilterComparison, ServiceRecord>();
+ final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
+ final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
- final ArrayList<ServiceRecord> mDelayedStartList
- = new ArrayList<ServiceRecord>();
+ final ArrayList<ServiceRecord> mDelayedStartList = new ArrayList<>();
/* XXX eventually I'd like to have this based on processes instead of services.
* That is, if we try to start two services in a row both running in the same
* process, this should be one entry in mStartingBackground for that one process
@@ -185,8 +182,7 @@
= new ArrayList<DelayingProcess>();
*/
- final ArrayList<ServiceRecord> mStartingBackground
- = new ArrayList<ServiceRecord>();
+ final ArrayList<ServiceRecord> mStartingBackground = new ArrayList<>();
static final int MSG_BG_START_TIMEOUT = 1;
@@ -338,7 +334,7 @@
ServiceRecord r = res.record;
if (!mAm.mUserController.exists(r.userId)) {
- Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
+ Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
@@ -510,6 +506,35 @@
return 0;
}
+ void stopInBackgroundLocked(int uid) {
+ // Stop all services associated with this uid due to it going to the background
+ // stopped state.
+ ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
+ ArrayList<ServiceRecord> stopping = null;
+ if (services != null) {
+ for (int i=services.mServicesByName.size()-1; i>=0; i--) {
+ ServiceRecord service = services.mServicesByName.valueAt(i);
+ if (service.appInfo.uid == uid && service.startRequested) {
+ if (mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+ uid, service.packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (stopping == null) {
+ stopping = new ArrayList<>();
+ stopping.add(service);
+ }
+ }
+ }
+ }
+ if (stopping != null) {
+ for (int i=stopping.size()-1; i>=0; i--) {
+ ServiceRecord service = stopping.get(i);
+ service.delayed = false;
+ services.ensureNotStartingBackground(service);
+ stopServiceLocked(service);
+ }
+ }
+ }
+ }
+
IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
@@ -1069,6 +1094,22 @@
}
r = smap.mServicesByName.get(name);
if (r == null && createIfNeeded) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Before going further -- if this app is not allowed to run in the
+ // background, then at this point we aren't going to let it period.
+ if (!mAm.checkAllowBackgroundLocked(sInfo.applicationInfo.uid,
+ sInfo.packageName, callingPid)) {
+ Slog.w(TAG, "Background execution not allowed: service "
+ + r.intent + " to " + name.flattenToShortString()
+ + " from pid=" + callingPid + " uid=" + callingUid
+ + " pkg=" + callingPackage);
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
@@ -1527,7 +1568,7 @@
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
- mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+ mAm.notifyPackageUse(r.serviceInfo.packageName);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 41d4271..557b386 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,84 +16,19 @@
package com.android.server.am;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
-import android.Manifest;
-import android.app.ActivityManager.StackId;
-import android.app.AppOpsManager;
-import android.app.ApplicationThreadNative;
-import android.app.BroadcastOptions;
-import android.app.IActivityContainer;
-import android.app.IActivityContainerCallback;
-import android.app.IAppTask;
-import android.app.ITaskStackListener;
-import android.app.ProfilerInfo;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.app.admin.IDevicePolicyManager;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
-import android.appwidget.AppWidgetManager;
-import android.content.pm.AppsQueryHelper;
-import android.content.pm.PermissionInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.os.ResultReceiver;
-import android.os.Trace;
-import android.os.TransactionTooLargeException;
-import android.os.WorkSource;
-import android.os.storage.IMountService;
-import android.os.storage.MountServiceInternal;
-import android.os.storage.StorageManager;
-import android.provider.Settings.Global;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionSession;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.view.Display;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.DumpHeapActivity;
+import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.ProcessStats;
+import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.IResultReceiver;
@@ -111,31 +46,25 @@
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
-import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.Installer;
-import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskThumbnailInfo;
import android.app.ActivityManagerInternal;
@@ -145,24 +74,37 @@
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
+import android.app.ApplicationThreadNative;
+import android.app.BroadcastOptions;
import android.app.Dialog;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
import android.app.IActivityController;
+import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
-import android.app.IUidObserver;
+import android.app.ITaskStackListener;
import android.app.IUiAutomationConnection;
+import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.backup.IBackupManager;
+import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.backup.IBackupManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
@@ -186,18 +128,24 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -215,22 +163,37 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.TransactionTooLargeException;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.WorkSource;
+import android.os.storage.IMountService;
+import android.os.storage.MountServiceInternal;
+import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionSession;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.LocaleList;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -238,13 +201,12 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import dalvik.system.VMRuntime;
-
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
@@ -274,6 +236,100 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEBUG_APP;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
+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.ActivityStackSupervisor.FORCE_FOCUS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -334,6 +390,11 @@
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+ // How long we will retain processes hosting content providers in the "last activity"
+ // state before allowing them to drop down to the regular cached LRU list. This is
+ // to avoid thrashing of provider processes under low memory situations.
+ static final int CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
+
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
@@ -385,6 +446,10 @@
// Maximum number of users we allow to be running at a time.
static final int MAX_RUNNING_USERS = 3;
+ // This is the amount of time we allow an app to settle after it goes into the background,
+ // before we start restricting what it can do.
+ static final int BACKGROUND_SETTLE_TIME = 1*60*1000;
+
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -504,6 +569,11 @@
*/
SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+ /**
+ * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+ */
+ String mDeviceOwnerName;
+
final UserController mUserController;
public class PendingAssistExtras extends Binder implements Runnable {
@@ -708,6 +778,12 @@
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
/**
+ * This is for verifying the UID report flow.
+ */
+ static final boolean VALIDATE_UID_STATES = true;
+ final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
+
+ /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -1196,7 +1272,9 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mForceResizableActivites;
+ boolean mForceResizableActivities;
+ boolean mSupportsFreeformWindowManagement;
+ boolean mTakeFullscreenScreenshots;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
@@ -1302,29 +1380,29 @@
}
}
- static final int SHOW_ERROR_MSG = 1;
- static final int SHOW_NOT_RESPONDING_MSG = 2;
- static final int SHOW_FACTORY_ERROR_MSG = 3;
+ static final int SHOW_ERROR_UI_MSG = 1;
+ static final int SHOW_NOT_RESPONDING_UI_MSG = 2;
+ static final int SHOW_FACTORY_ERROR_UI_MSG = 3;
static final int UPDATE_CONFIGURATION_MSG = 4;
static final int GC_BACKGROUND_PROCESSES_MSG = 5;
- static final int WAIT_FOR_DEBUGGER_MSG = 6;
+ static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
- static final int SHOW_UID_ERROR_MSG = 14;
- static final int SHOW_FINGERPRINT_ERROR_MSG = 15;
+ static final int SHOW_UID_ERROR_UI_MSG = 14;
+ static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
- static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
static final int UPDATE_HTTP_PROXY_MSG = 29;
- static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
- static final int DISPATCH_PROCESSES_CHANGED = 31;
- static final int DISPATCH_PROCESS_DIED = 32;
+ static final int SHOW_COMPAT_MODE_DIALOG_UI_MSG = 30;
+ static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
+ static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int REPORT_USER_SWITCH_MSG = 34;
static final int CONTINUE_USER_SWITCH_MSG = 35;
@@ -1338,20 +1416,21 @@
static final int SYSTEM_USER_CURRENT_MSG = 43;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
- static final int START_USER_SWITCH_MSG = 46;
+ static final int START_USER_SWITCH_UI_MSG = 46;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
- static final int DISMISS_DIALOG_MSG = 48;
+ static final int DISMISS_DIALOG_UI_MSG = 48;
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 49;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 50;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 51;
static final int DELETE_DUMPHEAP_MSG = 52;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 53;
- static final int DISPATCH_UIDS_CHANGED_MSG = 54;
+ static final int DISPATCH_UIDS_CHANGED_UI_MSG = 54;
static final int REPORT_TIME_TRACKER_MSG = 55;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
static final int APP_BOOST_DEACTIVATE_MSG = 58;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
+ static final int IDLE_UIDS_MSG = 60;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1386,7 +1465,7 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SHOW_ERROR_MSG: {
+ 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;
@@ -1431,7 +1510,7 @@
ensureBootCompleted();
} break;
- case SHOW_NOT_RESPONDING_MSG: {
+ case SHOW_NOT_RESPONDING_UI_MSG: {
synchronized (ActivityManagerService.this) {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
@@ -1463,7 +1542,7 @@
ensureBootCompleted();
} break;
- case SHOW_STRICT_MODE_VIOLATION_MSG: {
+ case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord) data.get("app");
@@ -1489,13 +1568,13 @@
}
ensureBootCompleted();
} break;
- case SHOW_FACTORY_ERROR_MSG: {
+ case SHOW_FACTORY_ERROR_UI_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
- case WAIT_FOR_DEBUGGER_MSG: {
+ case WAIT_FOR_DEBUGGER_UI_MSG: {
synchronized (ActivityManagerService.this) {
ProcessRecord app = (ProcessRecord)msg.obj;
if (msg.arg1 != 0) {
@@ -1515,7 +1594,7 @@
}
}
} break;
- case SHOW_UID_ERROR_MSG: {
+ case SHOW_UID_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
@@ -1523,11 +1602,11 @@
d.setTitle(mContext.getText(R.string.android_system_label));
d.setMessage(mContext.getText(R.string.system_error_wipe_data));
d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_MSG, d));
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
- case SHOW_FINGERPRINT_ERROR_MSG: {
+ case SHOW_FINGERPRINT_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
@@ -1535,11 +1614,11 @@
d.setTitle(mContext.getText(R.string.android_system_label));
d.setMessage(mContext.getText(R.string.system_error_manufacturer));
d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_MSG, d));
+ obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
- case SHOW_COMPAT_MODE_DIALOG_MSG: {
+ case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord ar = (ActivityRecord) msg.obj;
if (mCompatModeDialog != null) {
@@ -1567,26 +1646,26 @@
}
break;
}
- case START_USER_SWITCH_MSG: {
- mUserController.showUserSwitchDialog(msg.arg1, (String) msg.obj);
+ case START_USER_SWITCH_UI_MSG: {
+ mUserController.showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj);
break;
}
- case DISMISS_DIALOG_MSG: {
+ case DISMISS_DIALOG_UI_MSG: {
final Dialog d = (Dialog) msg.obj;
d.dismiss();
break;
}
- case DISPATCH_PROCESSES_CHANGED: {
+ case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
dispatchProcessesChanged();
break;
}
- case DISPATCH_PROCESS_DIED: {
+ case DISPATCH_PROCESS_DIED_UI_MSG: {
final int pid = msg.arg1;
final int uid = msg.arg2;
dispatchProcessDied(pid, uid);
break;
}
- case DISPATCH_UIDS_CHANGED_MSG: {
+ case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
}
@@ -2038,7 +2117,7 @@
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
- case APP_BOOST_DEACTIVATE_MSG : {
+ case APP_BOOST_DEACTIVATE_MSG: {
synchronized(ActivityManagerService.this) {
if (mIsBoosted) {
if (mBoostStartTime < (SystemClock.uptimeMillis() - APP_BOOST_TIMEOUT)) {
@@ -2052,6 +2131,9 @@
}
}
} break;
+ case IDLE_UIDS_MSG: {
+ idleUids();
+ } break;
}
}
};
@@ -2344,6 +2426,17 @@
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler);
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
+ new IAppOpsCallback.Stub() {
+ @Override public void opChanged(int op, int uid, String packageName) {
+ if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
+ if (mAppOpsService.checkOperation(op, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ runInBackgroundDisabled(uid);
+ }
+ }
+ }
+ });
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
@@ -2355,7 +2448,7 @@
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mConfiguration.setToDefaults();
- mConfiguration.setLocale(Locale.getDefault());
+ mConfiguration.setLocales(LocaleList.getDefault());
mConfigurationSeq = mConfiguration.seq = 1;
mProcessCpuTracker.init();
@@ -2758,7 +2851,7 @@
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
- msg.what = SHOW_COMPAT_MODE_DIALOG_MSG;
+ msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
msg.obj = r.task.askedCompatMode ? null : r;
mUiHandler.sendMessage(msg);
}
@@ -3059,12 +3152,10 @@
return proc;
}
- void ensurePackageDexOpt(String packageName) {
+ void notifyPackageUse(String packageName) {
IPackageManager pm = AppGlobals.getPackageManager();
try {
- if (pm.performDexOptIfNeeded(packageName, null /* instruction set */)) {
- mDidDexOpt = true;
- }
+ pm.notifyPackageUse(packageName);
} catch (RemoteException e) {
}
}
@@ -3331,15 +3422,6 @@
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
- String jitDebugProperty = SystemProperties.get("debug.usejit");
- if ("true".equals(jitDebugProperty)) {
- debugFlags |= Zygote.DEBUG_ENABLE_JIT;
- } else if (!"false".equals(jitDebugProperty)) {
- // If we didn't force disable by setting false, defer to the dalvik vm options.
- if ("true".equals(SystemProperties.get("dalvik.vm.usejit"))) {
- debugFlags |= Zygote.DEBUG_ENABLE_JIT;
- }
- }
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
if ("true".equals(genDebugInfoProperty)) {
debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
@@ -3791,8 +3873,10 @@
for (int i=0; i<N; i++) {
final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
mActiveUidChanges[i] = change;
- change.uidRecord.pendingChange = null;
- change.uidRecord = null;
+ if (change.uidRecord != null) {
+ change.uidRecord.pendingChange = null;
+ change.uidRecord = null;
+ }
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
@@ -3802,7 +3886,8 @@
if (mLocalPowerManager != null) {
for (int j=0; j<N; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
- if (item.gone) {
+ if (item.change == UidRecord.CHANGE_GONE
+ || item.change == UidRecord.CHANGE_GONE_IDLE) {
mLocalPowerManager.uidGone(item.uid);
} else {
mLocalPowerManager.updateUidProcState(item.uid, item.processState);
@@ -3814,19 +3899,66 @@
while (i > 0) {
i--;
final IUidObserver observer = mUidObservers.getBroadcastItem(i);
+ final int which = (Integer)mUidObservers.getBroadcastCookie(i);
if (observer != null) {
try {
for (int j=0; j<N; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
- if (item.gone) {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID gone uid=" + item.uid);
- observer.onUidGone(item.uid);
+ final int change = item.change;
+ UidRecord validateUid = null;
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid = mValidateUids.get(item.uid);
+ if (validateUid == null && change != UidRecord.CHANGE_GONE
+ && change != UidRecord.CHANGE_GONE_IDLE) {
+ validateUid = new UidRecord(item.uid);
+ mValidateUids.put(item.uid, validateUid);
+ }
+ }
+ if (change == UidRecord.CHANGE_IDLE
+ || change == UidRecord.CHANGE_GONE_IDLE) {
+ if ((which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID idle uid=" + item.uid);
+ observer.onUidIdle(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ if (validateUid != null) {
+ validateUid.idle = true;
+ }
+ }
+ } else if (change == UidRecord.CHANGE_ACTIVE) {
+ if ((which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID active uid=" + item.uid);
+ observer.onUidActive(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid.idle = false;
+ }
+ }
+ if (change == UidRecord.CHANGE_GONE
+ || change == UidRecord.CHANGE_GONE_IDLE) {
+ if ((which & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID gone uid=" + item.uid);
+ observer.onUidGone(item.uid);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ if (validateUid != null) {
+ mValidateUids.remove(item.uid);
+ }
+ }
} else {
- if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "UID CHANGED uid=" + item.uid
- + ": " + item.processState);
- observer.onUidStateChanged(item.uid, item.processState);
+ if ((which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+ "UID CHANGED uid=" + item.uid
+ + ": " + item.processState);
+ observer.onUidStateChanged(item.uid, item.processState);
+ }
+ if (VALIDATE_UID_STATES && i == 0) {
+ validateUid.curProcState = validateUid.setProcState
+ = item.processState;
+ }
}
}
} catch (RemoteException e) {
@@ -4185,11 +4317,13 @@
if (launchStackId != INVALID_STACK_ID) {
if (launchStackId == DOCKED_STACK_ID && bOptions != null) {
ActivityOptions activityOptions = new ActivityOptions(bOptions);
- mWindowManager.setDockedStackCreateMode(activityOptions.getDockCreateMode());
+ mWindowManager.setDockedStackCreateState(activityOptions.getDockCreateMode(),
+ null /* initialBounds */);
}
if (task.stack.mStackId != launchStackId) {
mStackSupervisor.moveTaskToStackLocked(
- taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+ taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
+ true /* animate */);
}
}
@@ -5096,7 +5230,7 @@
// 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_MSG;
+ msg.what = SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
@@ -5136,12 +5270,8 @@
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
-
- final DevicePolicyManagerInternal dpmi =
- LocalServices.getService(DevicePolicyManagerInternal.class);
- if (dpmi != null && dpmi.isDeviceAdminPackage(userId, packageName)) {
- throw new SecurityException(
- "Clearing DeviceAdmin/DeviceOwner/ProfileOwner data is forbidden.");
+ if (packageName != null && packageName.equals(mDeviceOwnerName)) {
+ throw new SecurityException("Clearing DeviceOwner data is forbidden.");
}
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
@@ -5879,7 +6009,7 @@
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + old.uidRecord);
- enqueueUidChangeLocked(old.uidRecord, true);
+ enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
mActiveUids.remove(uid);
}
old.uidRecord = null;
@@ -5905,7 +6035,7 @@
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
mActiveUids.put(proc.uid, uidRec);
- enqueueUidChangeLocked(uidRec, false);
+ enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
uidRec.numProcs++;
@@ -6137,11 +6267,11 @@
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
- ensurePackageDexOpt(app.instrumentationInfo != null
+ notifyPackageUse(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
if (app.instrumentationClass != null) {
- ensurePackageDexOpt(app.instrumentationClass.getPackageName());
+ notifyPackageUse(app.instrumentationClass.getPackageName());
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + mConfiguration);
@@ -6221,7 +6351,7 @@
if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
- ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
+ notifyPackageUse(mBackupTarget.appInfo.packageName);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
@@ -6713,9 +6843,11 @@
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
return null;
}
if (activity.finishing) {
+ Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
return null;
}
}
@@ -7267,6 +7399,36 @@
return readMet && writeMet;
}
+ public int getAppStartMode(int uid, String packageName) {
+ synchronized (this) {
+ boolean bg = checkAllowBackgroundLocked(uid, packageName, -1);
+ return bg ? ActivityManager.APP_START_MODE_NORMAL
+ : ActivityManager.APP_START_MODE_DISABLED;
+ }
+ }
+
+ boolean checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec == null || uidRec.idle) {
+ if (callingPid >= 0) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(callingPid);
+ }
+ if (proc != null && proc.curProcState < ActivityManager.PROCESS_STATE_RECEIVER) {
+ // Whoever is instigating this is in the foreground, so we will allow it
+ // to go through.
+ return true;
+ }
+ }
+ if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private ProviderInfo getProviderInfoLocked(String authority, int userHandle) {
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -8266,7 +8428,7 @@
if (app == null) return;
Message msg = Message.obtain();
- msg.what = WAIT_FOR_DEBUGGER_MSG;
+ msg.what = WAIT_FOR_DEBUGGER_UI_MSG;
msg.obj = app;
msg.arg1 = waiting ? 1 : 0;
mUiHandler.sendMessage(msg);
@@ -8647,6 +8809,7 @@
}
if (task.mResizeable != resizeable) {
task.mResizeable = resizeable;
+ mWindowManager.setTaskResizeable(taskId, resizeable);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mStackSupervisor.resumeTopActivitiesLocked();
}
@@ -8665,6 +8828,16 @@
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
+ int stackId = task.stack.mStackId;
+ // First, check if this is a non-resizeble task in docked stack or if the task size
+ // is affected by the docked stack changing size. If so, instead of resizing, we
+ // can only scroll the task. No need to update configuration.
+ if (bounds != null && !task.mResizeable
+ && mStackSupervisor.isStackDockedInEffect(stackId)) {
+ mWindowManager.scrollTask(task.taskId, bounds);
+ return;
+ }
+
// Place the task in the right stack if it isn't there already based on
// the requested bounds.
// The stack transition logic is:
@@ -8672,7 +8845,6 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- int stackId = task.stack.mStackId;
if (!StackId.isTaskResizeAllowed(stackId)) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
@@ -8730,12 +8902,19 @@
}
@Override
- public Bitmap getTaskDescriptionIcon(String filename) {
- if (!FileUtils.isValidExtFilename(filename)
- || !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
- throw new IllegalArgumentException("Bad filename: " + filename);
+ public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "getTaskDescriptionIcon");
}
- return mTaskPersister.getTaskDescriptionIcon(filename);
+ final File passedIconFile = new File(filePath);
+ final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
+ passedIconFile.getName());
+ if (!legitIconFile.getPath().equals(filePath)
+ || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
+ throw new IllegalArgumentException("Bad file path: " + filePath);
+ }
+ return mTaskPersister.getTaskDescriptionIcon(filePath);
}
@Override
@@ -9073,7 +9252,7 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r
+ " to stackId=" + stackId);
mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS,
- "moveActivityToStack");
+ "moveActivityToStack", true /* animate */);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9094,7 +9273,7 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS,
- "moveTaskToStack");
+ "moveTaskToStack", true /* animate */);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9111,9 +9290,13 @@
* and
* {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
+ * @param animate Whether we should play an animation for the moving the task
+ * @param initialBounds If the docked stack gets created, it will use these bounds for the
+ * docked stack. Pass {@code null} to use default bounds.
*/
@Override
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop) {
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"moveTaskToDockedStack()");
synchronized (this) {
@@ -9121,9 +9304,9 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateMode(createMode);
- mStackSupervisor.moveTaskToStackLocked(
- taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack");
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mStackSupervisor.moveTaskToStackLocked(taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS,
+ "moveTaskToDockedStack", animate);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9242,6 +9425,17 @@
}
@Override
+ public void updateDeviceOwner(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("updateDeviceOwner called from non-system process");
+ }
+ synchronized (this) {
+ mDeviceOwnerName = packageName;
+ }
+ }
+
+ @Override
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
@@ -9462,7 +9656,7 @@
app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
mProcessStats);
}
- ensurePackageDexOpt(cpi.applicationInfo.packageName);
+ notifyPackageUse(cpi.applicationInfo.packageName);
}
}
return providers;
@@ -9638,6 +9832,14 @@
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
return true;
}
@@ -11192,11 +11394,12 @@
}
}
- public void registerUidObserver(IUidObserver observer) {
+ @Override
+ public void registerUidObserver(IUidObserver observer, int which) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerUidObserver()");
synchronized (this) {
- mUidObservers.register(observer);
+ mUidObservers.register(observer, which);
}
}
@@ -11688,21 +11891,25 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- String debugApp = Settings.Global.getString(resolver, Settings.Global.DEBUG_APP);
- boolean waitForDebugger = Settings.Global.getInt(
- resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
- boolean alwaysFinishActivities = Settings.Global.getInt(
- resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- boolean forceRtl = Settings.Global.getInt(
- resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
- int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
- boolean forceResizable = Settings.Global.getInt(
- resolver, Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
- defaultForceResizable) != 0;
- // Transfer any global setting for forcing RTL layout, into a System Property
- SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+ final boolean freeformWindowManagement =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
- Configuration configuration = new Configuration();
+ final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
+ final String fsScreenshots = Settings.Secure.getString(resolver,
+ "overview_fullscreen_thumbnails");
+ final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
+ final boolean alwaysFinishActivities =
+ Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+ final boolean takeFullscreenScreenshots = fsScreenshots != null &&
+ Integer.parseInt(fsScreenshots) != 0;
+ final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+ final int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
+ final boolean forceResizable = Settings.Global.getInt(
+ resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, defaultForceResizable) != 0;
+ // Transfer any global setting for forcing RTL layout, into a System Property
+ SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+ final Configuration configuration = new Configuration();
Settings.System.getConfiguration(resolver, configuration);
if (forceRtl) {
// This will take care of setting the correct layout direction flags
@@ -11713,7 +11920,9 @@
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mForceResizableActivites = forceResizable;
+ mForceResizableActivities = forceResizable;
+ mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
+ mTakeFullscreenScreenshots = takeFullscreenScreenshots;
// This happens before any activities are started, so we can
// change mConfiguration in-place.
updateConfigurationLocked(configuration, null, true);
@@ -12035,7 +12244,7 @@
mTopData = null;
mTopComponent = null;
Message msg = Message.obtain();
- msg.what = SHOW_FACTORY_ERROR_MSG;
+ msg.what = SHOW_FACTORY_ERROR_UI_MSG;
msg.getData().putCharSequence("msg", errorMsg);
mUiHandler.sendMessage(msg);
}
@@ -12081,24 +12290,33 @@
}
}
- enableSystemUserApps();
-
// Start up initial activity.
mBooting = true;
+ // Enable home activity for system user, so that the system can always boot
+ if (UserManager.isSplitSystemUser()) {
+ ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
+ try {
+ AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
+ UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
startHomeActivityLocked(currentUserId, "systemReady");
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+ " data partition or your device will be unstable.");
- mUiHandler.obtainMessage(SHOW_UID_ERROR_MSG).sendToTarget();
+ mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
}
} catch (RemoteException e) {
}
if (!Build.isBuildConsistent()) {
Slog.e(TAG, "Build fingerprint is not consistent, warning user");
- mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_MSG).sendToTarget();
+ mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
}
long ident = Binder.clearCallingIdentity();
@@ -12134,43 +12352,6 @@
}
}
- private void enableSystemUserApps() {
- // For system user, enable apps based on the following conditions:
- // - app is whitelisted; or has no launcher icons; or has INTERACT_ACROSS_USERS permission
- // - app is not in the blacklist
- if (UserManager.isSplitSystemUser()) {
- AppsQueryHelper queryHelper = new AppsQueryHelper(mContext);
- Set<String> enableApps = new HashSet<>();
- enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
- | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
- | AppsQueryHelper.GET_DEFAULT_IMES,
- /* systemAppsOnly */ true, UserHandle.SYSTEM));
- ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
- enableApps.addAll(wlApps);
- ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
- enableApps.removeAll(blApps);
-
- List<String> systemApps = queryHelper.queryApps(0, /* systemAppsOnly */ true,
- UserHandle.SYSTEM);
- final int systemAppsSize = systemApps.size();
- for (int i = 0; i < systemAppsSize; i++) {
- String pName = systemApps.get(i);
- boolean enable = enableApps.contains(pName);
- try {
- if (enable) {
- AppGlobals.getPackageManager().installExistingPackageAsUser(pName,
- UserHandle.USER_SYSTEM);
- } else {
- AppGlobals.getPackageManager().deletePackageAsUser(pName, null,
- UserHandle.USER_SYSTEM, PackageManager.DELETE_SYSTEM_APP);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Error occured when processing package " + pName, e);
- }
- }
- }
- }
-
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace) {
app.crashing = true;
@@ -12416,7 +12597,7 @@
final long origId = Binder.clearCallingIdentity();
Message msg = Message.obtain();
- msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+ msg.what = SHOW_STRICT_MODE_VIOLATION_UI_MSG;
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("result", result);
data.put("app", r);
@@ -12873,7 +13054,7 @@
}
Message msg = Message.obtain();
- msg.what = SHOW_ERROR_MSG;
+ msg.what = SHOW_ERROR_UI_MSG;
HashMap data = new HashMap();
data.put("result", result);
data.put("app", r);
@@ -13502,6 +13683,39 @@
}
}
+ boolean dumpUids(PrintWriter pw, String dumpPackage, SparseArray<UidRecord> uids,
+ String header, boolean needSep) {
+ boolean printed = false;
+ int whichAppId = -1;
+ if (dumpPackage != null) {
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
+ dumpPackage, 0);
+ whichAppId = UserHandle.getAppId(info.uid);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ for (int i=0; i<uids.size(); i++) {
+ UidRecord uidRec = uids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" ");
+ pw.println(header);
+ needSep = true;
+ }
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ }
+ return printed;
+ }
+
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -13558,33 +13772,13 @@
}
if (mActiveUids.size() > 0) {
- boolean printed = false;
- int whichAppId = -1;
- if (dumpPackage != null) {
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
- dumpPackage, 0);
- whichAppId = UserHandle.getAppId(info.uid);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
+ if (dumpUids(pw, dumpPackage, mActiveUids, "UID states:", needSep)) {
+ printedAnything = needSep = true;
}
- for (int i=0; i<mActiveUids.size(); i++) {
- UidRecord uidRec = mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- if (!printed) {
- printed = true;
- if (needSep) {
- pw.println();
- }
- pw.println(" UID states:");
- needSep = true;
- printedAnything = true;
- }
- pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
- pw.print(": "); pw.println(uidRec);
+ }
+ if (mValidateUids.size() > 0) {
+ if (dumpUids(pw, dumpPackage, mValidateUids, "UID validation:", needSep)) {
+ printedAnything = needSep = true;
}
}
@@ -14475,6 +14669,9 @@
if (object1.first.setAdj != object2.first.setAdj) {
return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
}
+ if (object1.first.setProcState != object2.first.setProcState) {
+ return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
+ }
if (object1.second.intValue() != object2.second.intValue()) {
return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
}
@@ -15293,6 +15490,10 @@
pw.print(" Lost RAM: "); pw.println(stringifyKBSize(memInfo.getTotalSizeKb()
- totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb()));
+ } else {
+ pw.print("lostram,"); pw.println(memInfo.getTotalSizeKb()
+ - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+ - memInfo.getKernelUsedSizeKb());
}
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
@@ -15847,7 +16048,8 @@
mAvailProcessChanges.add(item);
}
}
- mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
+ null).sendToTarget();
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
@@ -17546,6 +17748,9 @@
UserHandle.USER_NULL);
}
+ // To cache the list of supported system locales
+ private String[] mSupportedSystemLocales = null;
+
/**
* Do either or both things: (1) change the current configuration, and (2)
* make sure the given activity is running with the (now) current
@@ -17569,11 +17774,14 @@
EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
- if (!initLocale && values.locale != null && values.userSetLocale) {
- final String languageTag = values.locale.toLanguageTag();
- SystemProperties.set("persist.sys.locale", languageTag);
+ if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+ if (mSupportedSystemLocales == null) {
+ mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+ }
+ final Locale locale = values.getLocales().getBestMatch(mSupportedSystemLocales);
+ SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- values.locale));
+ locale));
}
mConfigurationSeq++;
@@ -17874,7 +18082,7 @@
app.systemNoUi = false;
- final int PROCESS_STATE_TOP = mTopProcessState;
+ final int PROCESS_STATE_CUR_TOP = mTopProcessState;
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
@@ -17889,7 +18097,7 @@
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "top-activity";
foregroundActivities = true;
- procState = PROCESS_STATE_TOP;
+ procState = PROCESS_STATE_CUR_TOP;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -17943,8 +18151,8 @@
adj = ProcessList.VISIBLE_APP_ADJ;
app.adjType = "visible";
}
- if (procState > PROCESS_STATE_TOP) {
- procState = PROCESS_STATE_TOP;
+ if (procState > PROCESS_STATE_CUR_TOP) {
+ procState = PROCESS_STATE_CUR_TOP;
}
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.cached = false;
@@ -17962,8 +18170,8 @@
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
app.adjType = "pausing";
}
- if (procState > PROCESS_STATE_TOP) {
- procState = PROCESS_STATE_TOP;
+ if (procState > PROCESS_STATE_CUR_TOP) {
+ procState = PROCESS_STATE_CUR_TOP;
}
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.cached = false;
@@ -18001,7 +18209,8 @@
}
}
- if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+ || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -18392,6 +18601,18 @@
}
}
+ if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+ adj = ProcessList.PREVIOUS_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.cached = false;
+ app.adjType = "provider";
+ }
+ if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+
if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
// A client of one of our services or providers is in the top state. We
// *may* want to be in the top state, but not if we are already running in
@@ -18900,8 +19121,6 @@
}
}
}
- Process.setSwappiness(app.pid,
- app.curSchedGroup <= Process.THREAD_GROUP_BG_NONINTERACTIVE);
}
}
if (app.repForegroundActivities != app.foregroundActivities) {
@@ -19026,7 +19245,7 @@
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Enqueueing dispatch processes changed!");
- mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG).sendToTarget();
}
mPendingProcessChanges.add(item);
}
@@ -19045,29 +19264,46 @@
return success;
}
- private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) {
- if (uidRec.pendingChange == null) {
+ private final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
+ final UidRecord.ChangeItem pendingChange;
+ if (uidRec == null || uidRec.pendingChange == null) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"*** Enqueueing dispatch uid changed!");
- mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_MSG).sendToTarget();
+ mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_UI_MSG).sendToTarget();
}
final int NA = mAvailUidChanges.size();
if (NA > 0) {
- uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
+ pendingChange = mAvailUidChanges.remove(NA-1);
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Retrieving available item: " + uidRec.pendingChange);
+ "Retrieving available item: " + pendingChange);
} else {
- uidRec.pendingChange = new UidRecord.ChangeItem();
+ pendingChange = new UidRecord.ChangeItem();
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Allocating new item: " + uidRec.pendingChange);
+ "Allocating new item: " + pendingChange);
}
- uidRec.pendingChange.uidRecord = uidRec;
- uidRec.pendingChange.uid = uidRec.uid;
- mPendingUidChanges.add(uidRec.pendingChange);
+ if (uidRec != null) {
+ uidRec.pendingChange = pendingChange;
+ if (change == UidRecord.CHANGE_GONE && !uidRec.idle) {
+ // If this uid is going away, and we haven't yet reported it is gone,
+ // then do so now.
+ change = UidRecord.CHANGE_GONE_IDLE;
+ }
+ } else if (uid < 0) {
+ throw new IllegalArgumentException("No UidRecord or uid");
+ }
+ pendingChange.uidRecord = uidRec;
+ pendingChange.uid = uidRec != null ? uidRec.uid : uid;
+ mPendingUidChanges.add(pendingChange);
+ } else {
+ pendingChange = uidRec.pendingChange;
+ if (change == UidRecord.CHANGE_GONE && pendingChange.change == UidRecord.CHANGE_IDLE) {
+ change = UidRecord.CHANGE_GONE_IDLE;
+ }
}
- uidRec.pendingChange.gone = gone;
- uidRec.pendingChange.processState = uidRec.setProcState;
+ pendingChange.change = change;
+ pendingChange.processState = uidRec != null
+ ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
}
private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
@@ -19614,12 +19850,31 @@
// Update from any uid changes.
for (int i=mActiveUids.size()-1; i>=0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
+ int uidChange = UidRecord.CHANGE_PROCSTATE;
if (uidRec.setProcState != uidRec.curProcState) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Changes in " + uidRec + ": proc state from " + uidRec.setProcState
+ " to " + uidRec.curProcState);
+ if (ActivityManager.isProcStateBackground(uidRec.curProcState)) {
+ if (!ActivityManager.isProcStateBackground(uidRec.setProcState)) {
+ uidRec.lastBackgroundTime = nowElapsed;
+ if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
+ // Note: the background settle time is in elapsed realtime, while
+ // the handler time base is uptime. All this means is that we may
+ // stop background uids later than we had intended, but that only
+ // happens because the device was sleeping so we are okay anyway.
+ mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, BACKGROUND_SETTLE_TIME);
+ }
+ }
+ } else {
+ if (uidRec.idle) {
+ uidChange = UidRecord.CHANGE_ACTIVE;
+ uidRec.idle = false;
+ }
+ uidRec.lastBackgroundTime = 0;
+ }
uidRec.setProcState = uidRec.curProcState;
- enqueueUidChangeLocked(uidRec, false);
+ enqueueUidChangeLocked(uidRec, -1, uidChange);
}
}
@@ -19644,6 +19899,53 @@
}
}
+ final void idleUids() {
+ synchronized (this) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long maxBgTime = nowElapsed - BACKGROUND_SETTLE_TIME;
+ long nextTime = 0;
+ for (int i=mActiveUids.size()-1; i>=0; i--) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ final long bgTime = uidRec.lastBackgroundTime;
+ if (bgTime > 0 && !uidRec.idle) {
+ if (bgTime <= maxBgTime) {
+ uidRec.idle = true;
+ doStopUidLocked(uidRec.uid, uidRec);
+ } else {
+ if (nextTime == 0 || nextTime > bgTime) {
+ nextTime = bgTime;
+ }
+ }
+ }
+ }
+ if (nextTime > 0) {
+ mHandler.removeMessages(IDLE_UIDS_MSG);
+ mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+ nextTime + BACKGROUND_SETTLE_TIME - nowElapsed);
+ }
+ }
+ }
+
+ final void runInBackgroundDisabled(int uid) {
+ synchronized (this) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec != null) {
+ // This uid is actually running... should it be considered background now?
+ if (uidRec.idle) {
+ doStopUidLocked(uidRec.uid, uidRec);
+ }
+ } else {
+ // This uid isn't actually running... still send a report about it being "stopped".
+ doStopUidLocked(uid, null);
+ }
+ }
+ }
+
+ final void doStopUidLocked(int uid, final UidRecord uidRec) {
+ mServices.stopInBackgroundLocked(uid);
+ enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
+ }
+
final void trimApplications() {
synchronized (this) {
int i;
@@ -19963,24 +20265,32 @@
}
@Override
- public boolean switchUser(final int userId) {
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
- String userName;
+ public boolean unlockUser(int userId, byte[] token) {
+ return mUserController.unlockUser(userId, token);
+ }
+
+ @Override
+ public boolean switchUser(final int targetUserId) {
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
+ UserInfo currentUserInfo;
+ UserInfo targetUserInfo;
synchronized (this) {
- UserInfo userInfo = mUserController.getUserInfo(userId);
- if (userInfo == null) {
- Slog.w(TAG, "No user info for user #" + userId);
+ int currentUserId = mUserController.getCurrentUserIdLocked();
+ currentUserInfo = mUserController.getUserInfo(currentUserId);
+ targetUserInfo = mUserController.getUserInfo(targetUserId);
+ if (targetUserInfo == null) {
+ Slog.w(TAG, "No user info for user #" + targetUserId);
return false;
}
- if (userInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+ if (targetUserInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
return false;
}
- userName = userInfo.name;
- mUserController.setTargetUserIdLocked(userId);
+ mUserController.setTargetUserIdLocked(targetUserId);
}
- mUiHandler.removeMessages(START_USER_SWITCH_MSG);
- mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
+ Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
+ mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
+ mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_UI_MSG, userNames));
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d1e7e85..13c1417 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -73,7 +73,7 @@
String opt;
while ((opt = getNextOption()) != null) {
if (opt.equals("--user")) {
- userId = parseUserArg(getNextArgRequired());
+ userId = UserHandle.parseUserArg(getNextArgRequired());
} else {
pw.println("Error: Unknown option: " + opt);
return -1;
@@ -89,7 +89,7 @@
String opt;
while ((opt=getNextOption()) != null) {
if (opt.equals("--user")) {
- userId = parseUserArg(getNextArgRequired());
+ userId = UserHandle.parseUserArg(getNextArgRequired());
} else {
pw.println("Error: Unknown option: " + opt);
return -1;
@@ -141,22 +141,6 @@
return 0;
}
- int parseUserArg(String arg) {
- int userId;
- if ("all".equals(arg)) {
- userId = UserHandle.USER_ALL;
- } else if ("current".equals(arg) || "cur".equals(arg)) {
- userId = UserHandle.USER_CURRENT;
- } else {
- try {
- userId = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Bad user number: " + arg);
- }
- }
- return userId;
- }
-
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index aa04bd7..ea8a12f 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -61,6 +61,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -1212,8 +1213,10 @@
if (_taskDescription.getIconFilename() == null &&
(icon = _taskDescription.getIcon()) != null) {
final String iconFilename = createImageFilename(createTime, task.taskId);
- mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilename);
- _taskDescription.setIconFilename(iconFilename);
+ final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename);
+ final String iconFilePath = iconFile.getAbsolutePath();
+ mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilePath);
+ _taskDescription.setIconFilename(iconFilePath);
}
taskDescription = _taskDescription;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 400ebc6..cd29050 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -54,6 +54,7 @@
import android.app.ResultInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
@@ -72,6 +73,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
@@ -836,8 +838,8 @@
}
}
- public final Bitmap screenshotActivities(ActivityRecord who) {
- if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "screenshotActivities: " + who);
+ public final Bitmap screenshotActivitiesLocked(ActivityRecord who) {
+ if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "screenshotActivitiesLocked: " + who);
if (who.noDisplay) {
if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "\tNo display");
return null;
@@ -852,10 +854,21 @@
int w = mService.mThumbnailWidth;
int h = mService.mThumbnailHeight;
+ float scale = 1f;
if (w > 0) {
if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "\tTaking screenshot");
+
+ // When this flag is set, we currently take the fullscreen screenshot of the activity
+ // but scaled inversely by the density. This gives us a "good-enough" fullscreen
+ // thumbnail to use within SystemUI without using enormous amounts of memory on high
+ // density devices.
+ if (mService.mTakeFullscreenScreenshots) {
+ Context context = mService.mContext;
+ w = h = -1;
+ scale = (1f / Math.max(1f, context.getResources().getDisplayMetrics().density));
+ }
return mWindowManager.screenshotApplications(who.appToken, Display.DEFAULT_DISPLAY,
- w, h);
+ w, h, scale);
}
Slog.e(TAG, "Invalid thumbnail dimensions: " + w + "x" + h);
return null;
@@ -914,7 +927,7 @@
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
if (mService.mHasRecents
&& (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
- prev.updateThumbnailLocked(screenshotActivities(prev), null);
+ prev.updateThumbnailLocked(screenshotActivitiesLocked(prev), null);
}
stopFullyDrawnTraceIfNeeded();
@@ -1338,7 +1351,12 @@
return topHomeActivity == null || !topHomeActivity.isHomeActivity();
}
- final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
+ // Find the first stack below focused stack that actually got something visible.
+ int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
+ while (belowFocusedIndex >= 0 &&
+ mStacks.get(belowFocusedIndex).topRunningActivityLocked() == null) {
+ belowFocusedIndex--;
+ }
if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
&& stackIndex == belowFocusedIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
@@ -1413,19 +1431,9 @@
if (top == null) {
return;
}
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "ensureActivitiesVisible behind " + top
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
+ " configChanges=0x" + Integer.toHexString(configChanges));
-
- if (mTranslucentActivityWaiting != top) {
- mUndrawnActivitiesBelowTopTranslucent.clear();
- if (mTranslucentActivityWaiting != null) {
- // Call the callback with a timeout indication.
- notifyActivityDrawnLocked(null);
- mTranslucentActivityWaiting = null;
- }
- mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
- }
+ checkTranslucentActivityWaiting(top);
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
@@ -1453,7 +1461,6 @@
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Make visible? " + r + " finishing=" + r.finishing
+ " state=" + r.state);
-
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
@@ -1461,143 +1468,28 @@
}
if (r.app == null || r.app.thread == null) {
- // We need to make sure the app is running if it's the top, or it is
- // just made visible from invisible.
- // If the app is already visible, it must have died while it was visible.
- // In this case, we'll show the dead window but will not restart the app.
- // Otherwise we could end up thrashing.
- if (r == top || !r.visible) {
- // This activity needs to be visible, but isn't even running...
- // get it started and resume if no other stack in this stack is resumed.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible || r.mLaunchTaskBehind) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Starting and making visible: " + r);
- setVisible(r, true);
- }
- if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(
- r, noStackActivityResumed, false);
- if (activityNdx >= activities.size()) {
- // Record may be removed if its process needs to restart.
- activityNdx = activities.size() - 1;
- } else {
- noStackActivityResumed = false;
- }
+ if (makeVisibleAndRestartIfNeeded(starting, configChanges, top,
+ noStackActivityResumed, r)) {
+ if (activityNdx >= activities.size()) {
+ // Record may be removed if its process needs to restart.
+ activityNdx = activities.size() - 1;
+ } else {
+ noStackActivityResumed = false;
}
}
} else if (r.visible) {
- // If this activity is already visible, then there is nothing
- // else to do here.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Skipping: already visible at " + r);
- r.stopFreezingScreenLocked(false);
- try {
- if (r.returningOptions != null) {
- r.app.thread.scheduleOnNewActivityOptions(r.appToken,
- r.returningOptions);
- }
- } catch(RemoteException e) {
- }
- if (r.state == ActivityState.RESUMED) {
+ if (alreadyVisible(r)) {
noStackActivityResumed = false;
}
} else {
- // This activity is not currently visible, but is running.
- // Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it
- // to now show its window.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Making visible and scheduling visibility: " + r);
- try {
- if (mTranslucentActivityWaiting != null) {
- r.updateOptionsLocked(r.returningOptions);
- mUndrawnActivitiesBelowTopTranslucent.add(r);
- }
- setVisible(r, true);
- r.sleeping = false;
- r.app.pendingUiClean = true;
- r.app.thread.scheduleWindowVisibility(r.appToken, true);
- r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: "
- + r.intent.getComponent(), e);
- }
- }
+ becomeVisible(starting, r);
}
-
// Aggregate current change flags.
configChanges |= r.configChangeFlags;
-
- if (r.fullscreen) {
- // At this point, nothing else needs to be shown in this task.
- behindFullscreenActivity = true;
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
- + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
- behindFullscreenActivity = true;
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
- + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- }
+ behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
+ behindFullscreenActivity, task, r);
} else {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Make invisible? " + r + " finishing=" + r.finishing
- + " state=" + r.state + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
- // Now for any activities that aren't visible to the user, make
- // sure they no longer are keeping the screen frozen.
- if (r.visible) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r);
- try {
- setVisible(r, false);
- switch (r.state) {
- case STOPPING:
- case STOPPED:
- if (r.app != null && r.app.thread != null) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Scheduling invisibility: " + r);
- r.app.thread.scheduleWindowVisibility(r.appToken, false);
- }
- break;
-
- case INITIALIZING:
- case RESUMED:
- case PAUSING:
- case PAUSED:
- // This case created for transitioning activities from
- // translucent to opaque {@link Activity#convertToOpaque}.
- if (getVisibleBehindActivity() == r) {
- releaseBackgroundResources(r);
- } else {
- if (!mStackSupervisor.mStoppingActivities.contains(r)) {
- mStackSupervisor.mStoppingActivities.add(r);
- }
- mStackSupervisor.scheduleIdleLocked();
- }
- break;
-
- default:
- break;
- }
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making hidden: "
- + r.intent.getComponent(), e);
- }
- } else {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
- }
+ becomeInvisible(stackInvisible, behindFullscreenActivity, r);
}
}
if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
@@ -1615,6 +1507,147 @@
}
}
+ private void checkTranslucentActivityWaiting(ActivityRecord top) {
+ if (mTranslucentActivityWaiting != top) {
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ if (mTranslucentActivityWaiting != null) {
+ // Call the callback with a timeout indication.
+ notifyActivityDrawnLocked(null);
+ mTranslucentActivityWaiting = null;
+ }
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+ }
+ }
+
+ private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
+ ActivityRecord top, boolean noStackActivityResumed, ActivityRecord r) {
+ // We need to make sure the app is running if it's the top, or it is just made visible from
+ // invisible. If the app is already visible, it must have died while it was visible. In this
+ // case, we'll show the dead window but will not restart the app. Otherwise we could end up
+ // thrashing.
+ if (r == top || !r.visible) {
+ // This activity needs to be visible, but isn't even running...
+ // get it started and resume if no other stack in this stack is resumed.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible || r.mLaunchTaskBehind) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
+ setVisible(r, true);
+ }
+ if (r != starting) {
+ mStackSupervisor.startSpecificActivityLocked(r, noStackActivityResumed, false);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void becomeInvisible(boolean stackInvisible, boolean behindFullscreenActivity,
+ ActivityRecord r) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r + " finishing="
+ + r.finishing + " state=" + r.state + " stackInvisible=" + stackInvisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity);
+ if (!r.visible) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
+ return;
+ }
+ // Now for any activities that aren't visible to the user, make sure they no longer are
+ // keeping the screen frozen.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r);
+ try {
+ setVisible(r, false);
+ switch (r.state) {
+ case STOPPING:
+ case STOPPED:
+ if (r.app != null && r.app.thread != null) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Scheduling invisibility: " + r);
+ r.app.thread.scheduleWindowVisibility(r.appToken, false);
+ }
+ break;
+
+ case INITIALIZING:
+ case RESUMED:
+ case PAUSING:
+ case PAUSED:
+ // This case created for transitioning activities from
+ // translucent to opaque {@link Activity#convertToOpaque}.
+ if (getVisibleBehindActivity() == r) {
+ releaseBackgroundResources(r);
+ } else {
+ if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+ mStackSupervisor.mStoppingActivities.add(r);
+ }
+ mStackSupervisor.scheduleIdleLocked();
+ }
+ break;
+
+ default:
+ break;
+ }
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
+ }
+ }
+
+ private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
+ TaskRecord task, ActivityRecord r) {
+ if (r.fullscreen) {
+ // At this point, nothing else needs to be shown in this task.
+ behindFullscreenActivity = true;
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ + " stackInvisible=" + stackInvisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity);
+ } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
+ behindFullscreenActivity = true;
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
+ + " stackInvisible=" + stackInvisible
+ + " behindFullscreenActivity=" + behindFullscreenActivity);
+ }
+ return behindFullscreenActivity;
+ }
+
+ private void becomeVisible(ActivityRecord starting, ActivityRecord r) {
+ // This activity is not currently visible, but is running. Tell it to become visible.
+ r.visible = true;
+ if (r.state != ActivityState.RESUMED && r != starting) {
+ // If this activity is paused, tell it to now show its window.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Making visible and scheduling visibility: " + r);
+ try {
+ if (mTranslucentActivityWaiting != null) {
+ r.updateOptionsLocked(r.returningOptions);
+ mUndrawnActivitiesBelowTopTranslucent.add(r);
+ }
+ setVisible(r, true);
+ r.sleeping = false;
+ r.app.pendingUiClean = true;
+ r.app.thread.scheduleWindowVisibility(r.appToken, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
+ }
+ }
+ }
+
+ private boolean alreadyVisible(ActivityRecord r) {
+ // If this activity is already visible, then there is nothing else to do here.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping: already visible at " + r);
+ r.stopFreezingScreenLocked(false);
+ try {
+ if (r.returningOptions != null) {
+ r.app.thread.scheduleOnNewActivityOptions(r.appToken, r.returningOptions);
+ }
+ } catch(RemoteException e) {
+ }
+ return r.state == ActivityState.RESUMED;
+ }
+
void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -3103,6 +3136,9 @@
// If the activity is PAUSING, we will complete the finish once
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
+ if (r.visible) {
+ mWindowManager.setAppVisibility(r.appToken, false);
+ }
return finishCurrentActivityLocked(r, FINISH_AFTER_PAUSE, oomAdj) == null;
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
@@ -4611,14 +4647,20 @@
voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, false);
- if (mTaskPositioner != null) {
- mTaskPositioner.updateDefaultBounds(task, mTaskHistory, info.layout);
- } else if (mBounds != null && task.mResizeable) {
+ if (!layoutTaskInStack(task, info.layout) && mBounds != null && task.mResizeable) {
task.updateOverrideConfiguration(mBounds);
}
return task;
}
+ boolean layoutTaskInStack(TaskRecord task, ActivityInfo.Layout layout) {
+ if (mTaskPositioner == null) {
+ return false;
+ }
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory, layout);
+ return true;
+ }
+
ArrayList<TaskRecord> getAllTasks() {
return new ArrayList<>(mTaskHistory);
}
@@ -4658,6 +4700,7 @@
(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
bounds, task.mOverrideConfig, !r.isHomeActivity());
+ mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
@@ -4711,6 +4754,7 @@
task.updateOverrideConfiguration(bounds);
mWindowManager.setAppTask(
r.appToken, task.taskId, task.getLaunchBounds(), task.mOverrideConfig);
+ mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6acaa77..723c1a6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -34,6 +34,7 @@
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -99,7 +100,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
@@ -121,10 +121,13 @@
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
+import android.widget.Toast;
+
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TransferPipe;
+import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -132,7 +135,6 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -182,6 +184,7 @@
static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
+ static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = FIRST_SUPERVISOR_STACK_MSG + 14;
private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
@@ -1294,7 +1297,7 @@
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName());
r.sleeping = false;
r.forceNewConfig = false;
mService.showAskCompatModeDialogLocked(r);
@@ -1666,9 +1669,7 @@
UserInfo user = getUserInfo(userId);
// TODO: Timeout for work challenge
- if (user.isManagedProfile()
- && mService.mContext.getSystemService(StorageManager.class)
- .isPerUserEncryptionEnabled()) {
+ if (user.isManagedProfile() && StorageManager.isFileBasedEncryptionEnabled()) {
KeyguardManager km = (KeyguardManager) mService.mContext
.getSystemService(Context.KEYGUARD_SERVICE);
@@ -1926,9 +1927,9 @@
boolean overrideBounds = false;
Rect newBounds = null;
if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
- if (options.hasBounds()) {
+ if (canUseActivityOptionsLaunchBounds(options)) {
overrideBounds = true;
- newBounds = options.getBounds();
+ newBounds = options.getLaunchBounds();
}
}
@@ -2930,8 +2931,8 @@
}
if (task.mResizeable && options != null) {
- if (options.hasBounds()) {
- Rect bounds = options.getBounds();
+ if (canUseActivityOptionsLaunchBounds(options)) {
+ Rect bounds = options.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
final int stackId = task.getLaunchStackId();
if (stackId != task.stack.mStackId) {
@@ -2955,6 +2956,12 @@
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
}
+ private boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
+ // We use the launch bounds in the activity options is the device supports freeform
+ // window management.
+ return options.hasLaunchBounds() && mService.mSupportsFreeformWindowManagement;
+ }
+
ActivityStack getStack(int stackId) {
return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
}
@@ -3008,6 +3015,15 @@
return null;
}
+ /**
+ * Returns if a stack should be treated as if it's docked. Returns true if the stack is
+ * the docked stack itself, or if it's side-by-side to the docked stack.
+ */
+ boolean isStackDockedInEffect(int stackId) {
+ return stackId == DOCKED_STACK_ID ||
+ (StackId.isResizeableByDockedStack(stackId) && getStack(DOCKED_STACK_ID) != null);
+ }
+
ActivityContainer createVirtualActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
ActivityContainer activityContainer =
@@ -3097,7 +3113,8 @@
final int count = tasks.size();
for (int i = 0; i < count; i++) {
moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack");
+ FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
+ true /* animate */);
}
// stack shouldn't contain anymore activities, so nothing to resume.
@@ -3333,13 +3350,19 @@
}
void moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
- String reason) {
+ String reason, boolean animate) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return;
}
+ if (task.stack != null && task.stack.mStackId == stackId) {
+ // You are already in the right stack silly...
+ Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
+ return;
+ }
+
final ActivityRecord topActivity = task.getTopActivity();
if (StackId.preserveWindowOnTaskMove(stackId) && topActivity != null) {
// We are about to relaunch the activity because its configuration changed due to
@@ -3348,29 +3371,30 @@
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
// entrance of the new window to be properly animated.
- mWindowManager.setReplacingWindow(topActivity.appToken, true /* animate */);
+ mWindowManager.setReplacingWindow(topActivity.appToken, animate);
}
- final ActivityStack stack =
- moveTaskToStackUncheckedLocked(task, stackId, toTop, forceFocus,
- "moveTaskToStack:" + reason);
+ final ActivityStack stack = moveTaskToStackUncheckedLocked(
+ task, stackId, toTop, forceFocus, "moveTaskToStack:" + reason);
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- resizeTaskLocked(task, stack.mBounds,
- RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
+ resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
resizeTaskLocked(task, task.mLastNonFullscreenBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
} else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- resizeTaskLocked(task, stack.mBounds,
- RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
+ resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
}
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeTopActivitiesLocked();
+
+ if (!task.mResizeable && isStackDockedInEffect(stackId)) {
+ showNonResizeableDockToast();
+ }
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
@@ -3399,7 +3423,7 @@
// There is only one activity in the task. So, we can just move the task over to the
// pinned stack without re-parenting the activity in a different task.
moveTaskToStackLocked(task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS,
- "moveTopActivityToPinnedStack");
+ "moveTopActivityToPinnedStack", true /* animate */);
} else {
final ActivityStack pinnedStack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
pinnedStack.moveActivityToStack(r);
@@ -3420,9 +3444,12 @@
Slog.w(TAG, "positionTaskInStackLocked: no task for id=" + taskId);
return;
}
- ActivityStack stack =
- getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
- mWindowManager.positionTaskInStack(taskId, stackId, position);
+ final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, !ON_TOP);
+
+ task.updateOverrideConfigurationForStack(stack);
+
+ mWindowManager.positionTaskInStack(
+ taskId, stackId, position, task.mBounds, task.mOverrideConfig);
final boolean stackChanged = task.stack != null && task.stack != stack;
if (stackChanged) {
task.stack.removeTask(task, "moveTaskToStack", MOVING);
@@ -3681,7 +3708,7 @@
void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
r.mLaunchTaskBehind = false;
final TaskRecord task = r.task;
- task.setLastThumbnailLocked(task.stack.screenshotActivities(r));
+ task.setLastThumbnailLocked(task.stack.screenshotActivitiesLocked(r));
mRecentTasks.addLocked(task);
mService.notifyTaskStackChangedLocked();
mWindowManager.setAppVisibility(r.appToken, false);
@@ -4300,6 +4327,10 @@
}
}
+ void showNonResizeableDockToast() {
+ mHandler.sendEmptyMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST);
+ }
+
void showLockTaskToast() {
mLockTaskNotify.showToast(mLockTaskModeState);
}
@@ -4612,6 +4643,13 @@
}
}
} break;
+ case SHOW_NON_RESIZEABLE_DOCK_TOAST: {
+ final Toast toast = Toast.makeText(
+ mService.mContext,
+ mService.mContext.getString(R.string.dock_non_resizeble_text),
+ Toast.LENGTH_LONG);
+ toast.show();
+ } break;
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6de8579..fb37eda 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -269,7 +269,7 @@
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Delivering to component " + r.curComponent
+ ": " + r);
- mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
@@ -556,6 +556,17 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
+ if (!skip) {
+ if (!mService.checkAllowBackgroundLocked(filter.receiverList.uid, filter.packageName,
+ -1)) {
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")");
+ skip = true;
+ }
+ }
if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, filter.receiverList.uid)) {
@@ -938,6 +949,15 @@
skip = true;
}
if (!skip) {
+ if (!mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName, -1)) {
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ }
+ }
+ if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b77eec8..4bfe300 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -136,6 +136,7 @@
long curCpuTime; // How long proc has run CPU most recently
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
+ long lastProviderTime; // The last time someone else was using a provider in this process.
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
boolean cached; // Is this a cached process?
@@ -317,6 +318,11 @@
pw.print(" foregroundActivities="); pw.print(foregroundActivities);
pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
}
+ if (lastProviderTime > 0) {
+ pw.print(prefix); pw.print("lastProviderTime=");
+ TimeUtils.formatDuration(lastProviderTime, now, pw);
+ pw.println();
+ }
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 862a973..c63eaac 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -462,10 +462,12 @@
final boolean sameActivity = task.realActivity != null
&& tr.realActivity != null
&& task.realActivity.equals(tr.realActivity);
- if (!sameActivity) {
+ // If the document is open in another app or is not the same
+ // document, we don't need to trim it.
+ if (!sameActivity || !sameIntent) {
continue;
- }
- if (maxRecents > 0 && !doTrim) {
+ // Otherwise only trim if we are over our max recents for this task
+ } else if (maxRecents > 0 && !doTrim) {
--maxRecents;
continue;
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 150baf0..9a00075 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,10 +16,11 @@
package com.android.server.am;
-import android.content.pm.IPackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
+import android.os.Environment;
+import android.os.FileUtils;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -27,7 +28,6 @@
import android.util.Xml;
import android.os.Process;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -44,6 +44,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.List;
import libcore.io.IoUtils;
@@ -54,8 +55,10 @@
/** When not flushing don't write out files faster than this */
private static final long INTER_WRITE_DELAY_MS = 500;
- /** When not flushing delay this long before writing the first file out. This gives the next
- * task being launched a chance to load its resources without this occupying IO bandwidth. */
+ /**
+ * When not flushing delay this long before writing the first file out. This gives the next task
+ * being launched a chance to load its resources without this occupying IO bandwidth.
+ */
private static final long PRE_TASK_DELAY_MS = 3000;
/** The maximum number of entries to keep in the queue before draining it automatically. */
@@ -72,24 +75,23 @@
private static final String TAG_TASK = "task";
- static File sImagesDir;
- static File sTasksDir;
-
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final RecentTasks mRecentTasks;
- /** Value determines write delay mode as follows:
- * < 0 We are Flushing. No delays between writes until the image queue is drained and all
- * tasks needing persisting are written to disk. There is no delay between writes.
- * == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS.
- * > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be
- * delayed by #INTER_WRITE_DELAY_MS. */
+ /**
+ * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+ * until the image queue is drained and all tasks needing persisting are written to disk. There
+ * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+ * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+ * writes will be delayed by #INTER_WRITE_DELAY_MS.
+ */
private long mNextWriteTime = 0;
private final LazyTaskWriterThread mLazyTaskWriterThread;
private static class WriteQueueItem {}
+
private static class TaskWriteQueueItem extends WriteQueueItem {
final TaskRecord mTask;
@@ -97,12 +99,13 @@
mTask = task;
}
}
+
private static class ImageWriteQueueItem extends WriteQueueItem {
- final String mFilename;
+ final String mFilePath;
Bitmap mImage;
- ImageWriteQueueItem(String filename, Bitmap image) {
- mFilename = filename;
+ ImageWriteQueueItem(String filePath, Bitmap image) {
+ mFilePath = filePath;
mImage = image;
}
}
@@ -111,19 +114,18 @@
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
RecentTasks recentTasks) {
- sTasksDir = new File(systemDir, TASKS_DIRNAME);
- if (!sTasksDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
- if (!sTasksDir.mkdir()) {
- Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
+
+ final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
+ if (legacyImagesDir.exists()) {
+ if (!FileUtils.deleteContents(legacyImagesDir) || !legacyImagesDir.delete()) {
+ Slog.i(TAG, "Failure deleting legacy images directory: " + legacyImagesDir);
}
}
- sImagesDir = new File(systemDir, IMAGES_DIRNAME);
- if (!sImagesDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
- if (!sImagesDir.mkdir()) {
- Slog.e(TAG, "Failure creating images directory " + sImagesDir);
+ final File legacyTasksDir = new File(systemDir, TASKS_DIRNAME);
+ if (legacyTasksDir.exists()) {
+ if (!FileUtils.deleteContents(legacyTasksDir) || !legacyTasksDir.delete()) {
+ Slog.i(TAG, "Failure deleting legacy tasks directory: " + legacyTasksDir);
}
}
@@ -144,8 +146,8 @@
for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem &&
- ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
- if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
+ ((ImageWriteQueueItem) item).mFilePath.startsWith(taskString)) {
+ if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
" from write queue");
mWriteQueue.remove(queueNdx);
}
@@ -213,14 +215,14 @@
}
}
- void saveImage(Bitmap image, String filename) {
+ void saveImage(Bitmap image, String filePath) {
synchronized (this) {
int queueNdx;
for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilename.equals(filename)) {
+ if (imageWriteQueueItem.mFilePath.equals(filePath)) {
// replace the Bitmap with the new one.
imageWriteQueueItem.mImage = image;
break;
@@ -228,14 +230,14 @@
}
}
if (queueNdx < 0) {
- mWriteQueue.add(new ImageWriteQueueItem(filename, image));
+ mWriteQueue.add(new ImageWriteQueueItem(filePath, image));
}
if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
mNextWriteTime = FLUSH_QUEUE;
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+ if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
SystemClock.uptimeMillis() + " mNextWriteTime=" +
mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
@@ -244,22 +246,22 @@
yieldIfQueueTooDeep();
}
- Bitmap getTaskDescriptionIcon(String filename) {
+ Bitmap getTaskDescriptionIcon(String filePath) {
// See if it is in the write queue
- final Bitmap icon = getImageFromWriteQueue(filename);
+ final Bitmap icon = getImageFromWriteQueue(filePath);
if (icon != null) {
return icon;
}
- return restoreImage(filename);
+ return restoreImage(filePath);
}
- Bitmap getImageFromWriteQueue(String filename) {
+ Bitmap getImageFromWriteQueue(String filePath) {
synchronized (this) {
for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilename.equals(filename)) {
+ if (imageWriteQueueItem.mFilePath.equals(filePath)) {
return imageWriteQueueItem.mImage;
}
}
@@ -275,7 +277,7 @@
xmlSerializer.setOutput(stringWriter);
if (DEBUG) xmlSerializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
// save task
xmlSerializer.startDocument(null, true);
@@ -321,19 +323,22 @@
return null;
}
- ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
- final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+ private List<TaskRecord> restoreTasksForUserLocked(final int userId) {
+ final List<TaskRecord> tasks = new ArrayList<TaskRecord>();
ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
- File[] recentFiles = sTasksDir.listFiles();
+ File userTasksDir = getUserTasksDir(userId);
+
+ File[] recentFiles = userTasksDir.listFiles();
if (recentFiles == null) {
- Slog.e(TAG, "Unable to list files from " + sTasksDir);
+ Slog.e(TAG, "restoreTasksForUser: Unable to list files from " + userTasksDir);
return tasks;
}
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
File taskFile = recentFiles[taskNdx];
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+ if (DEBUG) Slog.d(TAG, "restoreTasksForUser: userId=" + userId
+ + ", taskFile=" + taskFile.getName());
BufferedReader reader = null;
boolean deleteFile = false;
try {
@@ -348,30 +353,29 @@
if (event == XmlPullParser.START_TAG) {
if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
if (TAG_TASK.equals(name)) {
- final TaskRecord task =
- TaskRecord.restoreFromXml(in, mStackSupervisor);
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
- task);
+ final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task="
+ + task);
if (task != null) {
// XXX Don't add to write queue... there is no reason to write
// out the stuff we just read, if we don't write it we will
// read the same thing again.
- //mWriteQueue.add(new TaskWriteQueueItem(task));
+ // mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.taskId;
mStackSupervisor.setNextTaskId(taskId);
// Check if it's a valid user id. Don't add tasks for removed users.
- if (ArrayUtils.contains(validUserIds, task.userId)) {
+ if (userId == task.userId) {
task.isPersistable = true;
tasks.add(task);
recoveredTaskIds.add(taskId);
}
} else {
- Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
- fileToString(taskFile));
+ Slog.e(TAG, "restoreTasksForUser: Unable to restore taskFile="
+ + taskFile + ": " + fileToString(taskFile));
}
} else {
- Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
- " name=" + name);
+ Slog.wtf(TAG, "restoreTasksForUser: Unknown xml event=" + event
+ + " name=" + name);
}
}
XmlUtils.skipCurrentTag(in);
@@ -390,10 +394,19 @@
}
if (!DEBUG) {
- removeObsoleteFiles(recoveredTaskIds);
+ removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
+ }
+ return tasks;
+ }
+
+ ArrayList<TaskRecord> restoreTasksLocked(final int[] validUserIds) {
+ final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+
+ for (int userId : validUserIds) {
+ tasks.addAll(restoreTasksForUserLocked(userId));
}
- // Fixup task affiliation from taskIds
+ // Fix up task affiliation from taskIds
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
@@ -420,7 +433,7 @@
}
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
" files=" + files);
if (files == null) {
Slog.e(TAG, "File error accessing recents directory (too many files open?).");
@@ -434,14 +447,14 @@
final int taskId;
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
} catch (Exception e) {
- Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
+ Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
file.delete();
continue;
}
if (!persistentTaskIds.contains(taskId)) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: deleting file=" + file.getName());
file.delete();
}
}
@@ -449,13 +462,39 @@
}
private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
- removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
- removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
+ for (int userId : mService.getRunningUserIds()) {
+ removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles());
+ removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles());
+ }
}
static Bitmap restoreImage(String filename) {
if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
- return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
+ return BitmapFactory.decodeFile(filename);
+ }
+
+ static File getUserTasksDir(int userId) {
+ File userTasksDir = new File(Environment.getUserSystemDirectory(userId), TASKS_DIRNAME);
+
+ if (!userTasksDir.exists()) {
+ if (!userTasksDir.mkdir()) {
+ Slog.e(TAG, "Failure creating tasks directory for user " + userId + ": "
+ + userTasksDir);
+ }
+ }
+ return userTasksDir;
+ }
+
+ static File getUserImagesDir(int userId) {
+ File userImagesDir = new File(Environment.getUserSystemDirectory(userId), IMAGES_DIRNAME);
+
+ if (!userImagesDir.exists()) {
+ if (!userImagesDir.mkdir()) {
+ Slog.e(TAG, "Failure creating images directory for user " + userId + ": "
+ + userImagesDir);
+ }
+ }
+ return userImagesDir;
}
private class LazyTaskWriterThread extends Thread {
@@ -508,7 +547,6 @@
INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
-
while (mWriteQueue.isEmpty()) {
if (mNextWriteTime != 0) {
mNextWriteTime = 0; // idle.
@@ -542,15 +580,15 @@
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- final String filename = imageWriteQueueItem.mFilename;
+ final String filePath = imageWriteQueueItem.mFilePath;
final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
FileOutputStream imageFile = null;
try {
- imageFile = new FileOutputStream(new File(sImagesDir, filename));
+ imageFile = new FileOutputStream(new File(filePath));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
} catch (Exception e) {
- Slog.e(TAG, "saveImage: unable to save " + filename, e);
+ Slog.e(TAG, "saveImage: unable to save " + filePath, e);
} finally {
IoUtils.closeQuietly(imageFile);
}
@@ -575,18 +613,21 @@
FileOutputStream file = null;
AtomicFile atomicFile = null;
try {
- atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(
- task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
+ atomicFile = new AtomicFile(new File(
+ getUserTasksDir(task.userId),
+ String.valueOf(task.taskId) + RECENTS_FILENAME
+ + TASK_EXTENSION));
file = atomicFile.startWrite();
file.write(stringWriter.toString().getBytes());
file.write('\n');
atomicFile.finishWrite(file);
+
} catch (IOException e) {
if (file != null) {
atomicFile.failWrite(file);
}
- Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +
- e);
+ Slog.e(TAG,
+ "Unable to open " + atomicFile + " for persisting. " + e);
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 22c3025..1e529dab 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -27,15 +27,23 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+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.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
-import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskThumbnailInfo;
@@ -58,8 +66,10 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
+
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -237,7 +247,8 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
mLastThumbnailInfo = new TaskThumbnailInfo();
taskId = _taskId;
mAffiliatedTaskId = _taskId;
@@ -256,7 +267,8 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
mLastThumbnailInfo = thumbnailInfo;
taskId = _taskId;
mAffiliatedTaskId = _taskId;
@@ -276,7 +288,6 @@
taskType = APPLICATION_ACTIVITY_TYPE;
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
- userId = UserHandle.getUserId(info.applicationInfo.uid);
lastTaskDescription = _taskDescription;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
@@ -294,7 +305,7 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(_userId), mFilename);
mLastThumbnailInfo = lastThumbnailInfo;
taskId = _taskId;
intent = _intent;
@@ -326,7 +337,7 @@
mNextAffiliateTaskId = nextTaskId;
mCallingUid = callingUid;
mCallingPackage = callingPackage;
- mResizeable = resizeable || mService.mForceResizableActivites;
+ mResizeable = resizeable || mService.mForceResizableActivities;
mPrivileged = privileged;
ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
@@ -428,7 +439,7 @@
} else {
autoRemoveRecents = false;
}
- mResizeable = info.resizeable || mService.mForceResizableActivites;
+ mResizeable = info.resizeable || mService.mForceResizableActivities;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
setLockTaskAuth();
@@ -537,7 +548,7 @@
mLastThumbnailFile.delete();
}
} else {
- mService.mTaskPersister.saveImage(thumbnail, mFilename);
+ mService.mTaskPersister.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath());
}
return true;
}
@@ -549,7 +560,8 @@
thumbs.thumbnailInfo = mLastThumbnailInfo;
thumbs.thumbnailFileDescriptor = null;
if (mLastThumbnail == null) {
- thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(mFilename);
+ thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(
+ mLastThumbnailFile.getAbsolutePath());
}
// Only load the thumbnail file if we don't have a thumbnail
if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
@@ -572,8 +584,8 @@
* Removes all associated thumbnail data when a task is removed or pruned from recents.
*/
void disposeThumbnail() {
+ mLastThumbnailInfo.reset();
mLastThumbnail = null;
- mLastThumbnailInfo = null;
lastDescription = null;
}
@@ -682,7 +694,7 @@
// Only set this based on the first activity
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
- if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivites) {
+ if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) {
mResizeable = r.info.resizeable;
}
isPersistable = r.isPersistable();
@@ -822,7 +834,7 @@
if (stack != null) {
final ActivityRecord resumedActivity = stack.mResumedActivity;
if (resumedActivity != null && resumedActivity.task == this) {
- final Bitmap thumbnail = stack.screenshotActivities(resumedActivity);
+ final Bitmap thumbnail = stack.screenshotActivitiesLocked(resumedActivity);
setLastThumbnailLocked(thumbnail);
}
}
@@ -1259,13 +1271,17 @@
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
- mBounds = new Rect(bounds);
+ if (mBounds == null) {
+ mBounds = new Rect(bounds);
+ } else {
+ mBounds.set(bounds);
+ }
if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
final Configuration serviceConfig = mService.mConfiguration;
- mOverrideConfig = new Configuration(serviceConfig);
+ mOverrideConfig = new Configuration(Configuration.EMPTY);
// TODO(multidisplay): Update Dp to that of display stack is on.
final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
mOverrideConfig.screenWidthDp =
@@ -1282,6 +1298,31 @@
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
+ /** Updates the task's bounds and override configuration to match what is expected for the
+ * input stack. */
+ void updateOverrideConfigurationForStack(ActivityStack inStack) {
+ if (stack != null && stack == inStack) {
+ return;
+ }
+
+ if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (!mResizeable) {
+ throw new IllegalArgumentException("Can not position non-resizeable task="
+ + this + " in stack=" + inStack);
+ }
+ if (mBounds != null) {
+ return;
+ }
+ if (mLastNonFullscreenBounds != null) {
+ updateOverrideConfiguration(mLastNonFullscreenBounds);
+ } else {
+ inStack.layoutTaskInStack(this, null);
+ }
+ } else {
+ updateOverrideConfiguration(inStack.mBounds);
+ }
+ }
+
/**
* Returns the correct stack to use based on task type and currently set bounds,
* regardless of the focused stack and current stack association of the task.
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index b4efbf0..d24c3a5 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -17,7 +17,9 @@
package com.android.server.am;
import android.app.ActivityManager;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.TimeUtils;
/**
* Overall information about a uid that has actively running processes.
@@ -26,12 +28,20 @@
final int uid;
int curProcState;
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ long lastBackgroundTime;
+ boolean idle;
int numProcs;
+ static final int CHANGE_PROCSTATE = 0;
+ static final int CHANGE_GONE = 1;
+ static final int CHANGE_GONE_IDLE = 2;
+ static final int CHANGE_IDLE = 3;
+ static final int CHANGE_ACTIVE = 4;
+
static final class ChangeItem {
UidRecord uidRecord;
int uid;
- boolean gone;
+ int change;
int processState;
}
@@ -54,9 +64,16 @@
UserHandle.formatUid(sb, uid);
sb.append(' ');
sb.append(ProcessList.makeProcStateString(curProcState));
- sb.append(" / ");
+ if (lastBackgroundTime > 0) {
+ sb.append(" bg:");
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
+ }
+ if (idle) {
+ sb.append(" idle");
+ }
+ sb.append(" procs:");
sb.append(numProcs);
- sb.append(" procs}");
+ sb.append("}");
return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index cbc13fe..b30905e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.os.Process.SYSTEM_UID;
@@ -57,13 +56,18 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.UserManagerService;
@@ -97,7 +101,9 @@
/**
* Which users have been started, so are allowed to run code.
*/
+ @GuardedBy("mService")
private final SparseArray<UserState> mStartedUsers = new SparseArray<>();
+
/**
* LRU list of history of current users. Most recently current is at the end.
*/
@@ -134,7 +140,9 @@
mService = service;
mHandler = mService.mHandler;
// User 0 is the first and only user that runs at boot.
- mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
+ final UserState uss = new UserState(UserHandle.SYSTEM);
+ mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
+ updateUserUnlockedState(uss);
mUserLru.add(UserHandle.USER_SYSTEM);
updateStartedUserArrayLocked();
}
@@ -409,6 +417,21 @@
return userManager;
}
+ private void updateUserUnlockedState(UserState uss) {
+ final IMountService mountService = IMountService.Stub
+ .asInterface(ServiceManager.getService("mount"));
+ if (mountService != null) {
+ try {
+ uss.unlocked = mountService.isUserKeyUnlocked(uss.mHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ } else {
+ // System isn't fully booted yet, so guess based on property
+ uss.unlocked = !StorageManager.isFileBasedEncryptionEnabled();
+ }
+ }
+
boolean startUser(final int userId, final boolean foreground) {
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
@@ -453,11 +476,14 @@
// If the user we are switching to is not currently started, then
// we need to start it now.
if (mStartedUsers.get(userId) == null) {
- mStartedUsers.put(userId, new UserState(new UserHandle(userId), false));
+ mStartedUsers.put(userId, new UserState(new UserHandle(userId)));
updateStartedUserArrayLocked();
needStart = true;
}
+ final UserState uss = mStartedUsers.get(userId);
+ updateUserUnlockedState(uss);
+
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
@@ -479,8 +505,6 @@
mUserLru.add(currentUserIdInt);
}
- final UserState uss = mStartedUsers.get(userId);
-
// Make sure user is in the started state. If it is currently
// stopping, we need to knock that off.
if (uss.mState == UserState.STATE_STOPPING) {
@@ -499,6 +523,9 @@
}
if (uss.mState == UserState.STATE_BOOTING) {
+ // Let user manager propagate user restrictions to other services.
+ getUserManager().onBeforeStartUser(userId);
+
// Booting up a new user, need to tell system services about it.
// Note that this is on the same handler as scheduling of broadcasts,
// which is important because it needs to go first.
@@ -586,10 +613,39 @@
return result;
}
- void showUserSwitchDialog(int userId, String userName) {
+ boolean unlockUser(final int userId, byte[] token) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: unlockUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ final UserInfo userInfo = getUserInfo(userId);
+ final IMountService mountService = IMountService.Stub
+ .asInterface(ServiceManager.getService("mount"));
+ try {
+ mountService.unlockUserKey(userId, userInfo.serialNumber, token);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+ throw e.rethrowAsRuntimeException();
+ }
+
+ synchronized (mService) {
+ final UserState uss = mStartedUsers.get(userId);
+ updateUserUnlockedState(uss);
+ }
+
+ return true;
+ }
+
+ void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
- Dialog d = new UserSwitchingDialog(mService, mService.mContext, userId, userName,
- true /* above system */);
+ Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromToUserPair.first,
+ fromToUserPair.second, true /* above system */);
d.show();
}
@@ -619,7 +675,7 @@
void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mService) {
- Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
}
}
@@ -956,8 +1012,11 @@
return true;
}
if ((flags & ActivityManager.FLAG_WITH_AMNESIA) != 0) {
- // TODO: add in amnesia lifecycle
- return false;
+ // If user is currently locked, we fall through to default "running"
+ // behavior below
+ if (state.unlocked) {
+ return false;
+ }
}
return state.mState != UserState.STATE_STOPPING
&& state.mState != UserState.STATE_SHUTDOWN;
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index b3d82bc..b5b5c1d 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -40,6 +40,7 @@
public int mState = STATE_BOOTING;
public boolean switching;
public boolean initializing;
+ public boolean unlocked;
/**
* The last time that a provider was reported to usage stats as being brought to important
@@ -47,7 +48,7 @@
*/
public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>();
- public UserState(UserHandle handle, boolean initial) {
+ public UserState(UserHandle handle) {
mHandle = handle;
}
@@ -62,6 +63,11 @@
}
if (switching) pw.print(" SWITCHING");
if (initializing) pw.print(" INITIALIZING");
+ if (unlocked) {
+ pw.print(" UNLOCKED");
+ } else {
+ pw.print(" LOCKED");
+ }
pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 28b4096..10e88e6 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -18,9 +18,12 @@
import android.app.AlertDialog;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -49,20 +52,26 @@
@GuardedBy("this")
private boolean mStartedUser;
- public UserSwitchingDialog(ActivityManagerService service, Context context,
- int userId, String userName, boolean aboveSystem) {
+ public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser,
+ UserInfo newUser, boolean aboveSystem) {
super(context);
mService = service;
- mUserId = userId;
+ mUserId = newUser.id;
// Set up the dialog contents
setCancelable(false);
Resources res = getContext().getResources();
// Custom view due to alignment and font size requirements
View view = LayoutInflater.from(getContext()).inflate(R.layout.user_switching_dialog, null);
- ((TextView) view.findViewById(R.id.message)).setText(
- res.getString(com.android.internal.R.string.user_switching_message, userName));
+
+ String viewMessage;
+ if (UserManager.isSplitSystemUser() && newUser.id == UserHandle.USER_SYSTEM) {
+ viewMessage = res.getString(R.string.user_logging_out_message, oldUser.name);
+ } else {
+ viewMessage = res.getString(R.string.user_switching_message, newUser.name);
+ }
+ ((TextView) view.findViewById(R.id.message)).setText(viewMessage);
setView(view);
if (aboveSystem) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 058d681..4f2f486 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -62,7 +62,6 @@
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
-import android.media.IRemoteControlDisplay;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.MediaPlayer;
@@ -667,8 +666,7 @@
mSettingsObserver = new SettingsObserver();
createStreamStates();
- mMediaFocusControl = new MediaFocusControl(mAudioHandler.getLooper(),
- mContext, mVolumeController, this);
+ mMediaFocusControl = new MediaFocusControl(mContext);
readAndSetLowRamDevice();
@@ -1068,6 +1066,7 @@
if (DEBUG_VOL) {
Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
}
+ setSystemAudioMute(masterMute);
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
@@ -5150,12 +5149,12 @@
UserInfo userInfo = UserManagerService.getInstance().getUserInfo(userId);
killBackgroundUserProcessesWithRecordAudioPermission(userInfo);
}
- UserManagerService.getInstance().setSystemControlledUserRestriction(
+ UserManagerService.getInstance().setUserRestriction(
UserManager.DISALLOW_RECORD_AUDIO, true, userId);
} else if (action.equals(Intent.ACTION_USER_FOREGROUND)) {
// Enable audio recording for foreground user/profile
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- UserManagerService.getInstance().setSystemControlledUserRestriction(
+ UserManagerService.getInstance().setUserRestriction(
UserManager.DISALLOW_RECORD_AUDIO, false, userId);
}
}
@@ -5234,36 +5233,6 @@
}
}
- //==========================================================================================
- // RemoteControlDisplay / RemoteControlClient / Remote info
- //==========================================================================================
- public boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- return mMediaFocusControl.registerRemoteController(rcd, w, h, listenerComp);
- }
-
- public boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- return mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h);
- }
-
- public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- mMediaFocusControl.unregisterRemoteControlDisplay(rcd);
- }
-
- public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- mMediaFocusControl.remoteControlDisplayUsesBitmapSize(rcd, w, h);
- }
-
- public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
- }
-
- @Override
- public void setRemoteStreamVolume(int index) {
- enforceVolumeController("set the remote stream volume");
- mMediaFocusControl.setRemoteStreamVolume(index);
- }
//==========================================================================================
// Audio Focus
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f72b598..d44d89d 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -16,52 +16,18 @@
package com.android.server.audio;
-import android.app.Activity;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.PendingIntent.OnFinished;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteControlDisplay;
-import android.media.IRemoteVolumeObserver;
-import android.media.RemoteControlClient;
import android.media.audiopolicy.IAudioPolicyCallback;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.IDeviceIdleController;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.speech.RecognizerIntent;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.Slog;
-import android.view.KeyEvent;
-
-import com.android.server.audio.PlayerRecord.RemotePlaybackState;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -74,329 +40,22 @@
* @hide
*
*/
-public class MediaFocusControl implements OnFinished {
+public class MediaFocusControl {
private static final String TAG = "MediaFocusControl";
- /** Debug remote control client/display feature */
- protected static final boolean DEBUG_RC = false;
- /** Debug volumes */
- protected static final boolean DEBUG_VOL = false;
-
- /** Used to alter media button redirection when the phone is ringing. */
- private boolean mIsRinging = false;
-
- private final PowerManager.WakeLock mMediaEventWakeLock;
- private final MediaEventHandler mEventHandler;
private final Context mContext;
- private final ContentResolver mContentResolver;
- private final AudioService.VolumeController mVolumeController;
private final AppOpsManager mAppOps;
- private final KeyguardManager mKeyguardManager;
- private final AudioService mAudioService;
- private final NotificationListenerObserver mNotifListenerObserver;
- protected MediaFocusControl(Looper looper, Context cntxt,
- AudioService.VolumeController volumeCtrl, AudioService as) {
- mEventHandler = new MediaEventHandler(looper);
+ protected MediaFocusControl(Context cntxt) {
mContext = cntxt;
- mContentResolver = mContext.getContentResolver();
- mVolumeController = volumeCtrl;
- mAudioService = as;
-
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
- int maxMusicLevel = as.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mMainRemote = new RemotePlaybackState(-1, maxMusicLevel, maxMusicLevel);
-
- // Register for phone state monitoring
- TelephonyManager tmgr = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
- tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
- mKeyguardManager =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mNotifListenerObserver = new NotificationListenerObserver();
-
- mHasRemotePlayback = false;
- mMainRemoteIsActive = false;
-
- PlayerRecord.setMediaFocusControl(this);
-
- postReevaluateRemote();
}
protected void dump(PrintWriter pw) {
pw.println("\nMediaFocusControl dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
- dumpRCStack(pw);
- dumpRCCStack(pw);
- dumpRCDList(pw);
- }
-
- //==========================================================================================
- // Management of RemoteControlDisplay registration permissions
- //==========================================================================================
- private final static Uri ENABLED_NOTIFICATION_LISTENERS_URI =
- Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
- private class NotificationListenerObserver extends ContentObserver {
-
- NotificationListenerObserver() {
- super(mEventHandler);
- mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (!ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri) || selfChange) {
- return;
- }
- if (DEBUG_RC) { Log.d(TAG, "NotificationListenerObserver.onChange()"); }
- postReevaluateRemoteControlDisplays();
- }
- }
-
- private final static int RCD_REG_FAILURE = 0;
- private final static int RCD_REG_SUCCESS_PERMISSION = 1;
- private final static int RCD_REG_SUCCESS_ENABLED_NOTIF = 2;
-
- /**
- * Checks a caller's authorization to register an IRemoteControlDisplay.
- * Authorization is granted if one of the following is true:
- * <ul>
- * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission</li>
- * <li>the caller's listener is one of the enabled notification listeners</li>
- * </ul>
- * @return RCD_REG_FAILURE if it's not safe to proceed with the IRemoteControlDisplay
- * registration.
- */
- private int checkRcdRegistrationAuthorization(ComponentName listenerComp) {
- // MEDIA_CONTENT_CONTROL permission check
- if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MEDIA_CONTENT_CONTROL)) {
- if (DEBUG_RC) { Log.d(TAG, "ok to register Rcd: has MEDIA_CONTENT_CONTROL permission");}
- return RCD_REG_SUCCESS_PERMISSION;
- }
-
- // ENABLED_NOTIFICATION_LISTENERS settings check
- if (listenerComp != null) {
- // this call is coming from an app, can't use its identity to read secure settings
- final long ident = Binder.clearCallingIdentity();
- try {
- final int currentUser = ActivityManager.getCurrentUser();
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
- if (enabledNotifListeners != null) {
- final String[] components = enabledNotifListeners.split(":");
- for (int i=0; i<components.length; i++) {
- final ComponentName component =
- ComponentName.unflattenFromString(components[i]);
- if (component != null) {
- if (listenerComp.equals(component)) {
- if (DEBUG_RC) { Log.d(TAG, "ok to register RCC: " + component +
- " is authorized notification listener"); }
- return RCD_REG_SUCCESS_ENABLED_NOTIF;
- }
- }
- }
- }
- if (DEBUG_RC) { Log.d(TAG, "not ok to register RCD, " + listenerComp +
- " is not in list of ENABLED_NOTIFICATION_LISTENERS"); }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return RCD_REG_FAILURE;
- }
-
- protected boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- int reg = checkRcdRegistrationAuthorization(listenerComp);
- if (reg != RCD_REG_FAILURE) {
- registerRemoteControlDisplay_int(rcd, w, h, listenerComp);
- return true;
- } else {
- Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
- ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
- " or be an enabled NotificationListenerService for registerRemoteController");
- return false;
- }
- }
-
- protected boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- int reg = checkRcdRegistrationAuthorization(null);
- if (reg != RCD_REG_FAILURE) {
- registerRemoteControlDisplay_int(rcd, w, h, null);
- return true;
- } else {
- Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
- ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
- " to register IRemoteControlDisplay");
- return false;
- }
- }
-
- private void postReevaluateRemoteControlDisplays() {
- sendMsg(mEventHandler, MSG_REEVALUATE_RCD, SENDMSG_QUEUE, 0, 0, null, 0);
- }
-
- private void onReevaluateRemoteControlDisplays() {
- if (DEBUG_RC) { Log.d(TAG, "onReevaluateRemoteControlDisplays()"); }
- // read which components are enabled notification listeners
- final int currentUser = ActivityManager.getCurrentUser();
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
- if (DEBUG_RC) { Log.d(TAG, " > enabled list: " + enabledNotifListeners); }
- synchronized(mAudioFocusLock) {
- synchronized(mPRStack) {
- // check whether the "enable" status of each RCD with a notification listener
- // has changed
- final String[] enabledComponents;
- if (enabledNotifListeners == null) {
- enabledComponents = null;
- } else {
- enabledComponents = enabledNotifListeners.split(":");
- }
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di =
- displayIterator.next();
- if (di.mClientNotifListComp != null) {
- boolean wasEnabled = di.mEnabled;
- di.mEnabled = isComponentInStringArray(di.mClientNotifListComp,
- enabledComponents);
- if (wasEnabled != di.mEnabled){
- try {
- // tell the RCD whether it's enabled
- di.mRcDisplay.setEnabled(di.mEnabled);
- // tell the RCCs about the change for this RCD
- enableRemoteControlDisplayForClient_syncRcStack(
- di.mRcDisplay, di.mEnabled);
- // when enabling, refresh the information on the display
- if (di.mEnabled) {
- sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
- di.mArtworkExpectedWidth /*arg1*/,
- di.mArtworkExpectedHeight/*arg2*/,
- di.mRcDisplay /*obj*/, 0/*delay*/);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error en/disabling RCD: ", e);
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * @param comp a non-null ComponentName
- * @param enabledArray may be null
- * @return
- */
- private boolean isComponentInStringArray(ComponentName comp, String[] enabledArray) {
- if (enabledArray == null || enabledArray.length == 0) {
- if (DEBUG_RC) { Log.d(TAG, " > " + comp + " is NOT enabled"); }
- return false;
- }
- final String compString = comp.flattenToString();
- for (int i=0; i<enabledArray.length; i++) {
- if (compString.equals(enabledArray[i])) {
- if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is enabled"); }
- return true;
- }
- }
- if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is NOT enabled"); }
- return false;
- }
-
- //==========================================================================================
- // Internal event handling
- //==========================================================================================
-
- // event handler messages
- private static final int MSG_RCDISPLAY_CLEAR = 1;
- private static final int MSG_RCDISPLAY_UPDATE = 2;
- private static final int MSG_REEVALUATE_REMOTE = 3;
- private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
- private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
- private static final int MSG_RCC_NEW_PLAYBACK_STATE = 6;
- private static final int MSG_RCC_SEEK_REQUEST = 7;
- private static final int MSG_RCC_UPDATE_METADATA = 8;
- private static final int MSG_RCDISPLAY_INIT_INFO = 9;
- private static final int MSG_REEVALUATE_RCD = 10;
- private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 11;
-
- // sendMsg() flags
- /** If the msg is already queued, replace it with this one. */
- private static final int SENDMSG_REPLACE = 0;
- /** If the msg is already queued, ignore this one and leave the old. */
- private static final int SENDMSG_NOOP = 1;
- /** If the msg is already queued, queue this one and leave the old. */
- private static final int SENDMSG_QUEUE = 2;
-
- private static void sendMsg(Handler handler, int msg,
- int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
-
- if (existingMsgPolicy == SENDMSG_REPLACE) {
- handler.removeMessages(msg);
- } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
- return;
- }
-
- handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
- }
-
- private class MediaEventHandler extends Handler {
- MediaEventHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_RCDISPLAY_CLEAR:
- onRcDisplayClear();
- break;
-
- case MSG_RCDISPLAY_UPDATE:
- // msg.obj is guaranteed to be non null
- onRcDisplayUpdate( (PlayerRecord) msg.obj, msg.arg1);
- break;
-
- case MSG_REEVALUATE_REMOTE:
- onReevaluateRemote();
- break;
-
- case MSG_RCC_NEW_VOLUME_OBS:
- onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
- (IRemoteVolumeObserver)msg.obj /* rvo */);
- break;
-
- case MSG_RCDISPLAY_INIT_INFO:
- // msg.obj is guaranteed to be non null
- onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
- msg.arg1/*w*/, msg.arg2/*h*/);
- break;
-
- case MSG_REEVALUATE_RCD:
- onReevaluateRemoteControlDisplays();
- break;
-
- case MSG_UNREGISTER_MEDIABUTTONINTENT:
- unregisterMediaButtonIntent( (PendingIntent) msg.obj );
- break;
- }
- }
}
@@ -406,25 +65,6 @@
private final static Object mAudioFocusLock = new Object();
- private final static Object mRingingLock = new Object();
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (state == TelephonyManager.CALL_STATE_RINGING) {
- //Log.v(TAG, " CALL_STATE_RINGING");
- synchronized(mRingingLock) {
- mIsRinging = true;
- }
- } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
- || (state == TelephonyManager.CALL_STATE_IDLE)) {
- synchronized(mRingingLock) {
- mIsRinging = false;
- }
- }
- }
- };
-
/**
* Discard the current audio focus owner.
* Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
@@ -865,1374 +505,4 @@
}
}
-
- //==========================================================================================
- // RemoteControl
- //==========================================================================================
- /**
- * No-op if the key code for keyEvent is not a valid media key
- * (see {@link #isValidMediaKeyEvent(KeyEvent)})
- * @param keyEvent the key event to send
- */
- protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
- }
-
- /**
- * No-op if the key code for keyEvent is not a valid media key
- * (see {@link #isValidMediaKeyEvent(KeyEvent)})
- * @param keyEvent the key event to send
- */
- protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
- }
-
- private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- // sanity check on the incoming key event
- if (!isValidMediaKeyEvent(keyEvent)) {
- Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
- return;
- }
- // event filtering for telephony
- synchronized(mRingingLock) {
- synchronized(mPRStack) {
- if ((mMediaReceiverForCalls != null) &&
- (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {
- dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
- return;
- }
- }
- }
- // event filtering based on voice-based interactions
- if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
- filterVoiceInputKeyEvent(keyEvent, needWakeLock);
- } else {
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to the telephony package.
- * Precondition: mMediaReceiverForCalls != null
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to one of the registered listeners,
- * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- synchronized(mPRStack) {
- if (!mPRStack.empty()) {
- // send the intent that was registered by the client
- try {
- mPRStack.peek().getMediaButtonIntent().send(mContext,
- needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
- keyIntent, this, mEventHandler);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending pending intent " + mPRStack.peek());
- e.printStackTrace();
- }
- } else {
- // legacy behavior when nobody registered their media button event receiver
- // through AudioManager
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone,
- mEventHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
- }
-
- /**
- * The different actions performed in response to a voice button key event.
- */
- private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
- private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
- private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
-
- private final Object mVoiceEventLock = new Object();
- private boolean mVoiceButtonDown;
- private boolean mVoiceButtonHandled;
-
- /**
- * Filter key events that may be used for voice-based interactions
- * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
- * media buttons that can be used to trigger voice-based interactions.
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (DEBUG_RC) {
- Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
- }
-
- int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
- int keyAction = keyEvent.getAction();
- synchronized (mVoiceEventLock) {
- if (keyAction == KeyEvent.ACTION_DOWN) {
- if (keyEvent.getRepeatCount() == 0) {
- // initial down
- mVoiceButtonDown = true;
- mVoiceButtonHandled = false;
- } else if (mVoiceButtonDown && !mVoiceButtonHandled
- && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- // long-press, start voice-based interactions
- mVoiceButtonHandled = true;
- voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
- }
- } else if (keyAction == KeyEvent.ACTION_UP) {
- if (mVoiceButtonDown) {
- // voice button up
- mVoiceButtonDown = false;
- if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
- voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
- }
- }
- }
- }//synchronized (mVoiceEventLock)
-
- // take action after media button event filtering for voice-based interactions
- switch (voiceButtonAction) {
- case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " ignore key event");
- break;
- case VOICEBUTTON_ACTION_START_VOICE_INPUT:
- if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
- // then start the voice-based interactions
- startVoiceBasedInteractions(needWakeLock);
- break;
- case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
- sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
- break;
- }
- }
-
- private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
- // send DOWN event
- KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- // send UP event
- keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
-
- }
-
- private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
- if (keyEvent == null) {
- return false;
- }
- return KeyEvent.isMediaKey(keyEvent.getKeyCode());
- }
-
- /**
- * Checks whether the given key code is one that can trigger the launch of voice-based
- * interactions.
- * @param keyCode the key code associated with the key event
- * @return true if the key is one of the supported voice-based interaction triggers
- */
- private static boolean isValidVoiceInputKeyCode(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Tell the system to start voice-based interactions / voice commands
- */
- private void startVoiceBasedInteractions(boolean needWakeLock) {
- Intent voiceIntent = null;
- // select which type of search to launch:
- // - screen on and device unlocked: action is ACTION_WEB_SEARCH
- // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
- // with EXTRA_SECURE set to true if the device is securely locked
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
- if (!isLocked && pm.isScreenOn()) {
- voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
- Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
- } else {
- IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- if (dic != null) {
- try {
- dic.exitIdle("voice-search");
- } catch (RemoteException e) {
- }
- }
- voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
- isLocked && mKeyguardManager.isKeyguardSecure());
- Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
- }
- // start the search activity
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- if (voiceIntent != null) {
- voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT);
- }
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "No activity for search: " + e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- if (needWakeLock) {
- mMediaEventWakeLock.release();
- }
- }
- }
-
- private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
-
- // only set when wakelock was acquired, no need to check value when received
- private static final String EXTRA_WAKELOCK_ACQUIRED =
- "android.media.AudioService.WAKELOCK_ACQUIRED";
-
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
- mMediaEventWakeLock.release();
- }
- }
-
- BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (intent == null) {
- return;
- }
- Bundle extras = intent.getExtras();
- if (extras == null) {
- return;
- }
- if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
- mMediaEventWakeLock.release();
- }
- }
- };
-
- /**
- * Synchronization on mCurrentRcLock always inside a block synchronized on mPRStack
- */
- private final Object mCurrentRcLock = new Object();
- /**
- * The one remote control client which will receive a request for display information.
- * This object may be null.
- * Access protected by mCurrentRcLock.
- */
- private IRemoteControlClient mCurrentRcClient = null;
- /**
- * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant
- * if mCurrentRcClient is null
- */
- private PendingIntent mCurrentRcClientIntent = null;
-
- private final static int RC_INFO_NONE = 0;
- private final static int RC_INFO_ALL =
- RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
-
- /**
- * A monotonically increasing generation counter for mCurrentRcClient.
- * Only accessed with a lock on mCurrentRcLock.
- * No value wrap-around issues as we only act on equal values.
- */
- private int mCurrentRcClientGen = 0;
-
-
- /**
- * Internal cache for the playback information of the RemoteControlClient whose volume gets to
- * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
- * every time we need this info.
- */
- private RemotePlaybackState mMainRemote;
- /**
- * Indicates whether the "main" RemoteControlClient is considered active.
- * Use synchronized on mMainRemote.
- */
- private boolean mMainRemoteIsActive;
- /**
- * Indicates whether there is remote playback going on. True even if there is no "active"
- * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
- * handles remote playback.
- * Use synchronized on mMainRemote.
- */
- private boolean mHasRemotePlayback;
-
- /**
- * The stack of remote control event receivers.
- * All read and write operations on mPRStack are synchronized.
- */
- private final Stack<PlayerRecord> mPRStack = new Stack<PlayerRecord>();
-
- /**
- * The component the telephony package can register so telephony calls have priority to
- * handle media button events
- */
- private ComponentName mMediaReceiverForCalls = null;
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control focus stack
- */
- private void dumpRCStack(PrintWriter pw) {
- pw.println("\nRemote Control stack entries (last is top of stack):");
- synchronized(mPRStack) {
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- stackIterator.next().dump(pw, true);
- }
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control stack, focusing
- * on RemoteControlClient data
- */
- private void dumpRCCStack(PrintWriter pw) {
- pw.println("\nRemote Control Client stack entries (last is top of stack):");
- synchronized(mPRStack) {
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- stackIterator.next().dump(pw, false);
- }
- synchronized(mCurrentRcLock) {
- pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
- }
- }
- synchronized (mMainRemote) {
- pw.println("\nRemote Volume State:");
- pw.println(" has remote: " + mHasRemotePlayback);
- pw.println(" is remote active: " + mMainRemoteIsActive);
- pw.println(" rccId: " + mMainRemote.mRccId);
- pw.println(" volume handling: "
- + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
- "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
- pw.println(" volume: " + mMainRemote.mVolume);
- pw.println(" volume steps: " + mMainRemote.mVolumeMax);
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the list of remote control displays
- */
- private void dumpRCDList(PrintWriter pw) {
- pw.println("\nRemote Control Display list entries:");
- synchronized(mPRStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- pw.println(" IRCD: " + di.mRcDisplay +
- " -- w:" + di.mArtworkExpectedWidth +
- " -- h:" + di.mArtworkExpectedHeight +
- " -- wantsPosSync:" + di.mWantsPositionSync +
- " -- " + (di.mEnabled ? "enabled" : "disabled"));
- }
- }
- }
-
- /**
- * Helper function:
- * Push the new media button receiver "near" the top of the PlayerRecord stack.
- * "Near the top" is defined as:
- * - at the top if the current PlayerRecord at the top is not playing
- * - below the entries at the top of the stack that correspond to the playing PlayerRecord
- * otherwise
- * Called synchronized on mPRStack
- * precondition: mediaIntent != null
- * @return true if the top of mPRStack was changed, false otherwise
- */
- private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
- ComponentName target, IBinder token) {
- if (mPRStack.empty()) {
- mPRStack.push(new PlayerRecord(mediaIntent, target, token));
- return true;
- } else if (mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
- // already at top of stack
- return false;
- }
- if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
- mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
- boolean topChanged = false;
- PlayerRecord prse = null;
- int lastPlayingIndex = mPRStack.size();
- int inStackIndex = -1;
- try {
- // go through the stack from the top to figure out who's playing, and the position
- // of this media button receiver (note that it may not be in the stack)
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- prse = mPRStack.elementAt(index);
- if (prse.isPlaybackActive()) {
- lastPlayingIndex = index;
- }
- if (prse.hasMatchingMediaButtonIntent(mediaIntent)) {
- inStackIndex = index;
- }
- }
-
- if (inStackIndex == -1) {
- // is not in stack
- prse = new PlayerRecord(mediaIntent, target, token);
- // it's new so it's not playing (no RemoteControlClient to give a playstate),
- // therefore it goes after the ones with active playback
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- // is in the stack
- if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
- prse = mPRStack.elementAt(inStackIndex);
- // remove it from its old location in the stack
- mPRStack.removeElementAt(inStackIndex);
- if (prse.isPlaybackActive()) {
- // and put it at the top
- mPRStack.push(prse);
- } else {
- // and put it after the ones with active playback
- if (inStackIndex > lastPlayingIndex) {
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- mPRStack.add(lastPlayingIndex - 1, prse);
- }
- }
- }
- }
-
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification or bad index
- Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
- + " size=" + mPRStack.size()
- + " accessing media button stack", e);
- }
-
- return (topChanged);
- }
-
- /**
- * Helper function:
- * Remove the remote control receiver from the RC focus stack.
- * Called synchronized on mPRStack
- * precondition: pi != null
- */
- private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) {
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.hasMatchingMediaButtonIntent(pi)) {
- prse.destroy();
- // ok to remove element while traversing the stack since we're leaving the loop
- mPRStack.removeElementAt(index);
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- */
- private boolean isCurrentRcController(PendingIntent pi) {
- if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(pi)) {
- return true;
- }
- return false;
- }
-
- //==========================================================================================
- // Remote control display / client
- //==========================================================================================
- /**
- * Update the remote control displays with the new "focused" client generation
- */
- private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- synchronized(mPRStack) {
- if (mRcDisplays.size() > 0) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- try {
- di.mRcDisplay.setCurrentClientId(
- newClientGeneration, newMediaIntent, clearing);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
- di.release();
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- /**
- * Update the remote control clients with the new "focused" client generation
- */
- private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
- // (using an iterator on the stack so we can safely remove an entry if needed,
- // traversal order doesn't matter here as we update all entries)
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord se = stackIterator.next();
- if ((se != null) && (se.getRcc() != null)) {
- try {
- se.getRcc().setCurrentClientGenerationId(newClientGeneration);
- } catch (RemoteException e) {
- Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
- stackIterator.remove();
- se.unlinkToRcClientDeath();
- }
- }
- }
- }
-
- /**
- * Update the displays and clients with the new "focused" client generation and name
- * @param newClientGeneration the new generation value matching a client update
- * @param newMediaIntent the media button event receiver associated with the client.
- * May be null, which implies there is no registered media button event receiver.
- * @param clearing true if the new client generation value maps to a remote control update
- * where the display should be cleared.
- */
- private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- // send the new valid client generation ID to all displays
- setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
- // send the new valid client generation ID to all clients
- setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_CLEAR event
- */
- private void onRcDisplayClear() {
- if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
-
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- null /*newMediaIntent*/, true /*clearing*/);
- }
- }
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_UPDATE event
- */
- private void onRcDisplayUpdate(PlayerRecord prse, int flags /* USED ?*/) {
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(prse.getRcc()))) {
- if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
-
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with
- // the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- prse.getMediaButtonIntent() /*newMediaIntent*/,
- false /*clearing*/);
-
- // tell the current client that it needs to send info
- try {
- //TODO change name to informationRequestForAllDisplays()
- mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: "+e);
- mCurrentRcClient = null;
- }
- } else {
- // the remote control display owner has changed between the
- // the message to update the display was sent, and the time it
- // gets to be processed (now)
- }
- }
- }
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_INIT_INFO event
- * Causes the current RemoteControlClient to send its info (metadata, playstate...) to
- * a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE.
- */
- private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) {
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if (mCurrentRcClient != null) {
- if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); }
- try {
- // synchronously update the new RCD with the current client generation
- // and matching PendingIntent
- newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent,
- false);
-
- // tell the current RCC that it needs to send info, but only to the new RCD
- try {
- mCurrentRcClient.informationRequestForDisplay(newRcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: ", e);
- mCurrentRcClient = null;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e);
- }
- }
- }
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- */
- private void clearRemoteControlDisplay_syncPrs() {
- synchronized(mCurrentRcLock) {
- mCurrentRcClient = null;
- }
- // will cause onRcDisplayClear() to be called in AudioService's handler thread
- mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
- }
-
- /**
- * Helper function for code readability: only to be called from
- * checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for
- * this method.
- * Preconditions:
- * - called synchronized on mPRStack
- * - mPRStack.isEmpty() is false
- */
- private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
- PlayerRecord prse = mPRStack.peek();
- int infoFlagsAboutToBeUsed = infoChangedFlags;
- // this is where we enforce opt-in for information display on the remote controls
- // with the new AudioManager.registerRemoteControlClient() API
- if (prse.getRcc() == null) {
- //Log.w(TAG, "Can't update remote control display with null remote control client");
- clearRemoteControlDisplay_syncPrs();
- return;
- }
- synchronized(mCurrentRcLock) {
- if (!prse.getRcc().equals(mCurrentRcClient)) {
- // new RC client, assume every type of information shall be queried
- infoFlagsAboutToBeUsed = RC_INFO_ALL;
- }
- mCurrentRcClient = prse.getRcc();
- mCurrentRcClientIntent = prse.getMediaButtonIntent();
- }
- // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
- mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
- infoFlagsAboutToBeUsed /* arg1 */, 0, prse /* obj, != null */) );
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- * Check whether the remote control display should be updated, triggers the update if required
- * @param infoChangedFlags the flags corresponding to the remote control client information
- * that has changed, if applicable (checking for the update conditions might trigger a
- * clear, rather than an update event).
- */
- private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
- // determine whether the remote control display should be refreshed
- // if the player record stack is empty, there is nothing to display, so clear the RC display
- if (mPRStack.isEmpty()) {
- clearRemoteControlDisplay_syncPrs();
- return;
- }
-
- // this is where more rules for refresh go
-
- // refresh conditions were verified: update the remote controls
- // ok to call: synchronized on mPRStack, mPRStack is not empty
- updateRemoteControlDisplay_syncPrs(infoChangedFlags);
- }
-
- /**
- * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
- * precondition: mediaIntent != null
- */
- protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
- IBinder token) {
- Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
-
- synchronized(mPRStack) {
- if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) {
- // new RC client, assume every type of information shall be queried
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
- * precondition: mediaIntent != null, eventReceiver != null
- */
- protected void unregisterMediaButtonIntent(PendingIntent mediaIntent)
- {
- Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
-
- synchronized(mPRStack) {
- boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
- removeMediaButtonReceiver_syncPrs(mediaIntent);
- if (topOfStackWillChange) {
- // current RC client will change, assume every type of info needs to be queried
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
- protected void unregisterMediaButtonIntentAsync(final PendingIntent mediaIntent) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_UNREGISTER_MEDIABUTTONINTENT, 0, 0,
- mediaIntent));
- }
-
- /**
- * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
- * precondition: c != null
- */
- protected void registerMediaButtonEventReceiverForCalls(ComponentName c) {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to register media button receiver for calls");
- return;
- }
- synchronized(mPRStack) {
- mMediaReceiverForCalls = c;
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
- */
- protected void unregisterMediaButtonEventReceiverForCalls() {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
- return;
- }
- synchronized(mPRStack) {
- mMediaReceiverForCalls = null;
- }
- }
-
- /**
- * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
- * @return the unique ID of the PlayerRecord associated with the RemoteControlClient
- * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
- * without modifying the RC stack, but while still causing the display to refresh (will
- * become blank as a result of this)
- */
- protected int registerRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient, String callingPackageName) {
- if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized(mPRStack) {
- // store the new display information
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if(prse.hasMatchingMediaButtonIntent(mediaIntent)) {
- prse.resetControllerInfoForRcc(rcClient, callingPackageName,
- Binder.getCallingUid());
-
- if (rcClient == null) {
- break;
- }
-
- rccId = prse.getRccId();
-
- // there is a new (non-null) client:
- // give the new client the displays (if any)
- if (mRcDisplays.size() > 0) {
- plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc());
- }
- break;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
-
- // if the eventReceiver is at the top of the stack
- // then check for potential refresh of the remote controls
- if (isCurrentRcController(mediaIntent)) {
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }//synchronized(mPRStack)
- return rccId;
- }
-
- /**
- * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
- * rcClient is guaranteed non-null
- */
- protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient) {
- if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
- synchronized(mPRStack) {
- boolean topRccChange = false;
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if ((prse.hasMatchingMediaButtonIntent(mediaIntent))
- && rcClient.equals(prse.getRcc())) {
- // we found the IRemoteControlClient to unregister
- prse.resetControllerInfoForNoRcc();
- topRccChange = (index == mPRStack.size()-1);
- // there can only be one matching RCC in the RC stack, we're done
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- if (topRccChange) {
- // no more RCC for the RCD, check for potential refresh of the remote controls
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
-
- /**
- * A class to encapsulate all the information about a remote control display.
- * After instanciation, init() must always be called before the object is added in the list
- * of displays.
- * Before being removed from the list of displays, release() must always be called (otherwise
- * it will leak death handlers).
- */
- private class DisplayInfoForServer implements IBinder.DeathRecipient {
- /** may never be null */
- private final IRemoteControlDisplay mRcDisplay;
- private final IBinder mRcDisplayBinder;
- private int mArtworkExpectedWidth = -1;
- private int mArtworkExpectedHeight = -1;
- private boolean mWantsPositionSync = false;
- private ComponentName mClientNotifListComp;
- private boolean mEnabled = true;
-
- public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
- if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
- mRcDisplay = rcd;
- mRcDisplayBinder = rcd.asBinder();
- mArtworkExpectedWidth = w;
- mArtworkExpectedHeight = h;
- }
-
- public boolean init() {
- try {
- mRcDisplayBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- // remote control display is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
- return false;
- }
- return true;
- }
-
- public void release() {
- try {
- mRcDisplayBinder.unlinkToDeath(this, 0);
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here, the display should have been unregistered anyway
- Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
- }
- }
-
- public void binderDied() {
- synchronized(mPRStack) {
- Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
- // remove the display from the list
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay == mRcDisplay) {
- if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
- displayIterator.remove();
- return;
- }
- }
- }
- }
- }
-
- /**
- * The remote control displays.
- * Access synchronized on mPRStack
- */
- private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
-
- /**
- * Plug each registered display into the specified client
- * @param rcc, guaranteed non null
- */
- private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- try {
- rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
- di.mArtworkExpectedHeight);
- if (di.mWantsPositionSync) {
- rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
- }
- }
- }
-
- private void enableRemoteControlDisplayForClient_syncRcStack(IRemoteControlDisplay rcd,
- boolean enabled) {
- // let all the remote control clients know whether the given display is enabled
- // (so the remote control stack traversal order doesn't matter).
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().enableRemoteControlDisplay(rcd, enabled);
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to client: ", e);
- }
- }
- }
- }
-
- /**
- * Is the remote control display interface already registered
- * @param rcd
- * @return true if the IRemoteControlDisplay is already in the list of displays
- */
- private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Register an IRemoteControlDisplay.
- * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
- * at the top of the stack to update the new display with its information.
- * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay to register. No effect if null.
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param listenerComp the component for the listener interface, may be null if it's not needed
- * to verify it belongs to one of the enabled notification listeners
- */
- private void registerRemoteControlDisplay_int(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
- synchronized(mAudioFocusLock) {
- synchronized(mPRStack) {
- if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
- return;
- }
- DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
- di.mEnabled = true;
- di.mClientNotifListComp = listenerComp;
- if (!di.init()) {
- if (DEBUG_RC) Log.e(TAG, " error registering RCD");
- return;
- }
- // add RCD to list of displays
- mRcDisplays.add(di);
-
- // let all the remote control clients know there is a new display (so the remote
- // control stack traversal order doesn't matter).
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().plugRemoteControlDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to client: ", e);
- }
- }
- }
-
- // we have a new display, of which all the clients are now aware: have it be
- // initialized wih the current gen ID and the current client info, do not
- // reset the information for the other (existing) displays
- sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
- w /*arg1*/, h /*arg2*/,
- rcd /*obj*/, 0/*delay*/);
- }
- }
- }
-
- /**
- * Unregister an IRemoteControlDisplay.
- * No effect if the IRemoteControlDisplay hasn't been successfully registered.
- * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
- * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
- */
- protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
- synchronized(mPRStack) {
- if (rcd == null) {
- return;
- }
-
- boolean displayWasPluggedIn = false;
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext() && !displayWasPluggedIn) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- displayWasPluggedIn = true;
- di.release();
- displayIterator.remove();
- }
- }
-
- if (displayWasPluggedIn) {
- // disconnect this remote control display from all the clients, so the remote
- // control stack traversal order doesn't matter
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().unplugRemoteControlDisplay(rcd);
- } catch (RemoteException e) {
- Log.e(TAG, "Error disconnecting remote control display to client: ", e);
- }
- }
- }
- } else {
- if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
- }
- }
- }
-
- /**
- * Update the size of the artwork used by an IRemoteControlDisplay.
- * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay with the new artwork size requirement
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mPRStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- boolean artworkSizeUpdate = false;
- while (displayIterator.hasNext() && !artworkSizeUpdate) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- artworkSizeUpdate = true;
- }
- }
- }
- if (artworkSizeUpdate) {
- // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
- // stack traversal order doesn't matter
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().setBitmapSizeForDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
- }
- }
- }
- }
- }
- }
-
- /**
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. Not null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- synchronized(mPRStack) {
- boolean rcdRegistered = false;
- // store the information about this display
- // (display stack traversal order doesn't matter).
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mWantsPositionSync = wantsSync;
- rcdRegistered = true;
- break;
- }
- }
- if (!rcdRegistered) {
- return;
- }
- // notify all current RemoteControlClients
- // (stack traversal order doesn't matter as we notify all RCCs)
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while (stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if (prse.getRcc() != null) {
- try {
- prse.getRcc().setWantsSyncForDisplay(rcd, wantsSync);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
- }
- }
- }
- }
- }
-
- // handler for MSG_RCC_NEW_VOLUME_OBS
- private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- synchronized(mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- prse.mRemoteVolumeObs = rvo;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- }
-
- /**
- * Checks if a remote client is active on the supplied stream type. Update the remote stream
- * volume state if found and playing
- * @param streamType
- * @return false if no remote playing is currently playing
- */
- protected boolean checkUpdateRemoteStateIfActive(int streamType) {
- synchronized(mPRStack) {
- // iterating from top of stack as active playback is more likely on entries at the top
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if ((prse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
- && isPlaystateActive(prse.mPlaybackState.mState)
- && (prse.mPlaybackStream == streamType)) {
- if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
- + ", vol =" + prse.mPlaybackVolume);
- synchronized (mMainRemote) {
- mMainRemote.mRccId = prse.getRccId();
- mMainRemote.mVolume = prse.mPlaybackVolume;
- mMainRemote.mVolumeMax = prse.mPlaybackVolumeMax;
- mMainRemote.mVolumeHandling = prse.mPlaybackVolumeHandling;
- mMainRemoteIsActive = true;
- }
- return true;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- }
- synchronized (mMainRemote) {
- mMainRemoteIsActive = false;
- }
- return false;
- }
-
- /**
- * Returns true if the given playback state is considered "active", i.e. it describes a state
- * where playback is happening, or about to
- * @param playState the playback state to evaluate
- * @return true if active, false otherwise (inactive or unknown)
- */
- protected static boolean isPlaystateActive(int playState) {
- switch (playState) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return true;
- default:
- return false;
- }
- }
-
- private void sendVolumeUpdateToRemote(int rccId, int direction) {
- if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
- if (direction == 0) {
- // only handling discrete events
- return;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (prse.getRccId() == rccId) {
- rvo = prse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(direction, -1);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching relative volume update", e);
- }
- }
- }
-
- protected int getRemoteStreamMaxVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolumeMax;
- }
- }
-
- protected int getRemoteStreamVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolume;
- }
- }
-
- protected void setRemoteStreamVolume(int vol) {
- if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return;
- }
- rccId = mMainRemote.mRccId;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (prse.getRccId() == rccId) {
- rvo = prse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(0, vol);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching absolute volume update", e);
- }
- }
- }
-
- /**
- * Call to make AudioService reevaluate whether it's in a mode where remote players should
- * have their volume controlled. In this implementation this is only to reset whether
- * VolumePanel should display remote volumes
- */
- protected void postReevaluateRemote() {
- sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
- }
-
- private void onReevaluateRemote() {
- // TODO This was used to notify VolumePanel if there was remote playback
- // in the stack. This is now in MediaSessionService. More code should be
- // removed.
- }
-
}
diff --git a/services/core/java/com/android/server/audio/PlayerRecord.java b/services/core/java/com/android/server/audio/PlayerRecord.java
deleted file mode 100644
index e98f12e..0000000
--- a/services/core/java/com/android/server/audio/PlayerRecord.java
+++ /dev/null
@@ -1,360 +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.server.audio;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.media.AudioManager;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteVolumeObserver;
-import android.media.RemoteControlClient;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.PrintWriter;
-
-/**
- * @hide
- * Class to handle all the information about a media player, encapsulating information
- * about its use RemoteControlClient, playback type and volume... The lifecycle of each
- * instance is managed by android.media.MediaFocusControl, from its addition to the player stack
- * stack to its release.
- */
-class PlayerRecord implements DeathRecipient {
-
- // on purpose not using this classe's name, as it will only be used from MediaFocusControl
- private static final String TAG = "MediaFocusControl";
- private static final boolean DEBUG = false;
-
- /**
- * A global counter for RemoteControlClient identifiers
- */
- private static int sLastRccId = 0;
-
- public static MediaFocusControl sController;
-
- /**
- * The target for the ACTION_MEDIA_BUTTON events.
- * Always non null. //FIXME verify
- */
- final private PendingIntent mMediaIntent;
- /**
- * The registered media button event receiver.
- */
- final private ComponentName mReceiverComponent;
-
- private int mRccId = -1;
-
- /**
- * A non-null token implies this record tracks a "live" player whose death is being monitored.
- */
- private IBinder mToken;
- private String mCallingPackageName;
- private int mCallingUid;
- /**
- * Provides access to the information to display on the remote control.
- * May be null (when a media button event receiver is registered,
- * but no remote control client has been registered) */
- private IRemoteControlClient mRcClient;
- private RcClientDeathHandler mRcClientDeathHandler;
- /**
- * Information only used for non-local playback
- */
- //FIXME private?
- public int mPlaybackType;
- public int mPlaybackVolume;
- public int mPlaybackVolumeMax;
- public int mPlaybackVolumeHandling;
- public int mPlaybackStream;
- public RccPlaybackState mPlaybackState;
- public IRemoteVolumeObserver mRemoteVolumeObs;
-
-
- protected static class RccPlaybackState {
- public int mState;
- public long mPositionMs;
- public float mSpeed;
-
- public RccPlaybackState(int state, long positionMs, float speed) {
- mState = state;
- mPositionMs = positionMs;
- mSpeed = speed;
- }
-
- public void reset() {
- mState = RemoteControlClient.PLAYSTATE_STOPPED;
- mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
- mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
- }
-
- @Override
- public String toString() {
- return stateToString() + ", " + posToString() + ", " + mSpeed + "X";
- }
-
- private String posToString() {
- if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) {
- return "PLAYBACK_POSITION_INVALID";
- } else if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
- return "PLAYBACK_POSITION_ALWAYS_UNKNOWN";
- } else {
- return (String.valueOf(mPositionMs) + "ms");
- }
- }
-
- private String stateToString() {
- switch (mState) {
- case RemoteControlClient.PLAYSTATE_NONE:
- return "PLAYSTATE_NONE";
- case RemoteControlClient.PLAYSTATE_STOPPED:
- return "PLAYSTATE_STOPPED";
- case RemoteControlClient.PLAYSTATE_PAUSED:
- return "PLAYSTATE_PAUSED";
- case RemoteControlClient.PLAYSTATE_PLAYING:
- return "PLAYSTATE_PLAYING";
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return "PLAYSTATE_FAST_FORWARDING";
- case RemoteControlClient.PLAYSTATE_REWINDING:
- return "PLAYSTATE_REWINDING";
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return "PLAYSTATE_SKIPPING_FORWARDS";
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return "PLAYSTATE_SKIPPING_BACKWARDS";
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- return "PLAYSTATE_BUFFERING";
- case RemoteControlClient.PLAYSTATE_ERROR:
- return "PLAYSTATE_ERROR";
- default:
- return "[invalid playstate]";
- }
- }
- }
-
-
- /**
- * Inner class to monitor remote control client deaths, and remove the client for the
- * remote control stack if necessary.
- */
- private class RcClientDeathHandler implements IBinder.DeathRecipient {
- final private IBinder mCb; // To be notified of client's death
- //FIXME needed?
- final private PendingIntent mMediaIntent;
-
- RcClientDeathHandler(IBinder cb, PendingIntent pi) {
- mCb = cb;
- mMediaIntent = pi;
- }
-
- public void binderDied() {
- Log.w(TAG, " RemoteControlClient died");
- // remote control client died, make sure the displays don't use it anymore
- // by setting its remote control client to null
- sController.registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
- // the dead client was maybe handling remote playback, the controller should reevaluate
- sController.postReevaluateRemote();
- }
-
- public IBinder getBinder() {
- return mCb;
- }
- }
-
-
- protected static class RemotePlaybackState {
- int mRccId;
- int mVolume;
- int mVolumeMax;
- int mVolumeHandling;
-
- protected RemotePlaybackState(int id, int vol, int volMax) {
- mRccId = id;
- mVolume = vol;
- mVolumeMax = volMax;
- mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- }
- }
-
-
- void dump(PrintWriter pw, boolean registrationInfo) {
- if (registrationInfo) {
- pw.println(" pi: " + mMediaIntent +
- " -- pack: " + mCallingPackageName +
- " -- ercvr: " + mReceiverComponent +
- " -- client: " + mRcClient +
- " -- uid: " + mCallingUid +
- " -- type: " + mPlaybackType +
- " state: " + mPlaybackState);
- } else {
- // emphasis on state
- pw.println(" uid: " + mCallingUid +
- " -- id: " + mRccId +
- " -- type: " + mPlaybackType +
- " -- state: " + mPlaybackState +
- " -- vol handling: " + mPlaybackVolumeHandling +
- " -- vol: " + mPlaybackVolume +
- " -- volMax: " + mPlaybackVolumeMax +
- " -- volObs: " + mRemoteVolumeObs);
- }
- }
-
-
- static protected void setMediaFocusControl(MediaFocusControl mfc) {
- sController = mfc;
- }
-
- /** precondition: mediaIntent != null */
- protected PlayerRecord(PendingIntent mediaIntent, ComponentName eventReceiver, IBinder token)
- {
- mMediaIntent = mediaIntent;
- mReceiverComponent = eventReceiver;
- mToken = token;
- mCallingUid = -1;
- mRcClient = null;
- mRccId = ++sLastRccId;
- mPlaybackState = new RccPlaybackState(
- RemoteControlClient.PLAYSTATE_STOPPED,
- RemoteControlClient.PLAYBACK_POSITION_INVALID,
- RemoteControlClient.PLAYBACK_SPEED_1X);
-
- resetPlaybackInfo();
- if (mToken != null) {
- try {
- mToken.linkToDeath(this, 0);
- } catch (RemoteException e) {
- sController.unregisterMediaButtonIntentAsync(mMediaIntent);
- }
- }
- }
-
- //---------------------------------------------
- // Accessors
- protected int getRccId() {
- return mRccId;
- }
-
- protected IRemoteControlClient getRcc() {
- return mRcClient;
- }
-
- protected ComponentName getMediaButtonReceiver() {
- return mReceiverComponent;
- }
-
- protected PendingIntent getMediaButtonIntent() {
- return mMediaIntent;
- }
-
- protected boolean hasMatchingMediaButtonIntent(PendingIntent pi) {
- if (mToken != null) {
- return mMediaIntent.equals(pi);
- } else {
- if (mReceiverComponent != null) {
- return mReceiverComponent.equals(pi.getIntent().getComponent());
- } else {
- return false;
- }
- }
- }
-
- protected boolean isPlaybackActive() {
- return MediaFocusControl.isPlaystateActive(mPlaybackState.mState);
- }
-
- //---------------------------------------------
- // Modify the records stored in the instance
- protected void resetControllerInfoForRcc(IRemoteControlClient rcClient,
- String callingPackageName, int uid) {
- // already had a remote control client?
- if (mRcClientDeathHandler != null) {
- // stop monitoring the old client's death
- unlinkToRcClientDeath();
- }
- // save the new remote control client
- mRcClient = rcClient;
- mCallingPackageName = callingPackageName;
- mCallingUid = uid;
- if (rcClient == null) {
- // here mcse.mRcClientDeathHandler is null;
- resetPlaybackInfo();
- } else {
- IBinder b = mRcClient.asBinder();
- RcClientDeathHandler rcdh =
- new RcClientDeathHandler(b, mMediaIntent);
- try {
- b.linkToDeath(rcdh, 0);
- } catch (RemoteException e) {
- // remote control client is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
- mRcClient = null;
- }
- mRcClientDeathHandler = rcdh;
- }
- }
-
- protected void resetControllerInfoForNoRcc() {
- // stop monitoring the RCC death
- unlinkToRcClientDeath();
- // reset the RCC-related fields
- mRcClient = null;
- mCallingPackageName = null;
- }
-
- public void resetPlaybackInfo() {
- mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
- mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- mPlaybackStream = AudioManager.STREAM_MUSIC;
- mPlaybackState.reset();
- mRemoteVolumeObs = null;
- }
-
- //---------------------------------------------
- public void unlinkToRcClientDeath() {
- if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
- try {
- mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
- mRcClientDeathHandler = null;
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here
- Log.e(TAG, "Error in unlinkToRcClientDeath()", e);
- }
- }
- }
-
- // FIXME rename to "release"? (as in FocusRequester class)
- public void destroy() {
- unlinkToRcClientDeath();
- if (mToken != null) {
- mToken.unlinkToDeath(this, 0);
- mToken = null;
- }
- }
-
- @Override
- public void binderDied() {
- sController.unregisterMediaButtonIntentAsync(mMediaIntent);
- }
-
- @Override
- protected void finalize() throws Throwable {
- destroy(); // unlink exception handled inside method
- super.finalize();
- }
-}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c1aaf07..6687412 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1353,18 +1353,9 @@
if (iface != null) {
String[] dnsServers = mDefaultDnsServers;
Collection<InetAddress> dnses = linkProperties.getDnsServers();
- if (dnses != null) {
- // we currently only handle IPv4
- ArrayList<InetAddress> v4Dnses =
- new ArrayList<InetAddress>(dnses.size());
- for (InetAddress dnsAddress : dnses) {
- if (dnsAddress instanceof Inet4Address) {
- v4Dnses.add(dnsAddress);
- }
- }
- if (v4Dnses.size() > 0) {
- dnsServers = NetworkUtils.makeStrings(v4Dnses);
- }
+ if (dnses != null && !dnses.isEmpty()) {
+ // TODO: remove this invocation of NetworkUtils.makeStrings().
+ dnsServers = NetworkUtils.makeStrings(dnses);
}
try {
Network network = getConnectivityManager().getNetworkForType(upType);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index b766894..75a74c0 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -466,7 +466,7 @@
if (cname == null) {
info = new SyncStorageEngine.EndPoint(account, authority, userId);
} else {
- info = new SyncStorageEngine.EndPoint(cname, userId);
+ info = new SyncStorageEngine.EndPoint(cname, userId, -1);
}
syncManager.clearScheduledSyncOperations(info);
syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 82e0eaf..4f53882 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -21,8 +21,10 @@
import android.accounts.AccountManager;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -79,11 +81,14 @@
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.android.server.accounts.AccountManagerService;
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.EndPoint;
@@ -207,6 +212,7 @@
volatile private boolean mDataConnectionIsConnected = false;
volatile private boolean mStorageIsLow = false;
volatile private boolean mDeviceIsIdle = false;
+ volatile private boolean mReportedSyncActive = false;
private final NotificationManager mNotificationMgr;
private AlarmManager mAlarmService = null;
@@ -234,7 +240,7 @@
private final AppIdleMonitor mAppIdleMonitor;
- private BroadcastReceiver mStorageIntentReceiver =
+ private final BroadcastReceiver mStorageIntentReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -257,7 +263,7 @@
}
};
- private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
boolean idle = mPowerManager.isDeviceIdleMode()
|| mPowerManager.isLightDeviceIdleMode();
@@ -267,12 +273,18 @@
SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
null /* any sync */);
} else {
+ if (mLocalDeviceIdleController != null) {
+ if (!mReportedSyncActive) {
+ mReportedSyncActive = true;
+ mLocalDeviceIdleController.setSyncActive(true);
+ }
+ }
sendCheckAlarmsMessage();
}
}
};
- private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mBootCompleted = true;
@@ -280,7 +292,7 @@
}
};
- private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateRunningAccounts();
@@ -291,7 +303,23 @@
}
};
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ cancelSyncsForUid(uid);
+ }
+ };
+
private final PowerManager mPowerManager;
+ DeviceIdleController.LocalService mLocalDeviceIdleController;
// Use this as a random offset to seed all periodic syncs.
private int mSyncRandomOffsetMillis;
@@ -494,6 +522,13 @@
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -651,6 +686,26 @@
Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
}
+ final android.content.pm.ServiceInfo sinfo;
+ try {
+ sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Not scheduling sync " + cname
+ + " -- can't find service for user " + userId);
+ return;
+ }
+ final int sUid = sinfo.applicationInfo.uid;
+
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(sUid, cname.getPackageName())
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling sync " + sUid + ":" + cname
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
+
Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
if (expedited) {
runtimeMillis = -1; // this means schedule at the front of the queue
@@ -678,7 +733,7 @@
}
return;
}
- SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId);
+ SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId, sUid);
Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
long delayUntil = mSyncStorageEngine.getDelayUntilTime(info);
final long backoffTime = backoff != null ? backoff.first : 0;
@@ -692,7 +747,7 @@
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(cname, userId, uid, source, extras,
+ new SyncOperation(cname, userId, sUid, cname.getPackageName(), uid, source, extras,
runtimeMillis /* runtime */,
beforeRunTimeMillis /* flextime */,
backoffTime,
@@ -827,6 +882,18 @@
if (syncAdapterInfo == null) {
continue;
}
+ final int owningUid = syncAdapterInfo.uid;
+ final String owningPackage = syncAdapterInfo.componentName.getPackageName();
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(owningUid,
+ owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
+ + syncAdapterInfo.componentName
+ + " -- package not allowed to start");
+ return;
+ }
+ } catch (RemoteException e) {
+ }
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
if (isSyncable < 0 && isAlwaysSyncable) {
@@ -876,7 +943,8 @@
+ ", extras " + newExtras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, reason, source,
+ new SyncOperation(account.account, account.userId,
+ owningUid, owningPackage, reason, source,
authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
backoffTime, delayUntil, allowParallelSyncs));
}
@@ -892,7 +960,8 @@
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, reason, source,
+ new SyncOperation(account.account, account.userId,
+ owningUid, owningPackage, reason, source,
authority, extras, runtimeMillis, beforeRuntimeMillis,
backoffTime, delayUntil, allowParallelSyncs));
}
@@ -1299,6 +1368,14 @@
}
}
+ void cancelSyncsForUid(int uid) {
+ synchronized (mSyncQueue) {
+ if (mSyncQueue.removeUidIfNeededLocked(uid)) {
+ sendCheckAlarmsMessage();
+ }
+ }
+ }
+
/**
* @hide
*/
@@ -1470,6 +1547,7 @@
}
pw.print("memory low: "); pw.println(mStorageIsLow);
pw.print("device idle: "); pw.println(mDeviceIsIdle);
+ pw.print("reported active: "); pw.println(mReportedSyncActive);
final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
@@ -2155,8 +2233,8 @@
/**
* Stash any messages that come to the handler before boot is complete or before the device
* is properly provisioned (i.e. out of set-up wizard).
- * {@link #onBootCompleted()} and {@link #onDeviceProvisioned(boolean)} both need to come
- * in before we start syncing.
+ * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
+ * need to come in before we start syncing.
* @param msg Message to dispatch at a later point.
* @return true if a message was enqueued, false otherwise. This is to avoid losing the
* message if we manage to acquire the lock but by the time we do boot has completed.
@@ -2492,6 +2570,8 @@
}
scheduleSyncOperation(
new SyncOperation(target.account, target.userId,
+ syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(),
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
target.provider, extras,
@@ -2502,6 +2582,7 @@
} else if (target.target_service) {
scheduleSyncOperation(
new SyncOperation(target.service, target.userId,
+ target.serviceUid, target.service.getPackageName(),
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
extras,
@@ -2544,6 +2625,7 @@
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
}
+ setSyncActive(false);
return Long.MAX_VALUE;
}
@@ -2551,6 +2633,7 @@
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: memory low, skipping");
}
+ setSyncActive(false);
return Long.MAX_VALUE;
}
@@ -2558,6 +2641,7 @@
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: device idle, skipping");
}
+ setSyncActive(false);
return Long.MAX_VALUE;
}
@@ -2567,6 +2651,7 @@
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
}
+ setSyncActive(false);
return Long.MAX_VALUE;
}
@@ -2772,9 +2857,25 @@
dispatchSyncOperation(candidate);
}
+ setSyncActive(mActiveSyncContexts.size() > 0);
+
return nextReadyToRunTime;
}
+ void setSyncActive(boolean active) {
+ if (mLocalDeviceIdleController == null) {
+ mLocalDeviceIdleController
+ = LocalServices.getService(DeviceIdleController.LocalService.class);
+ }
+ if (mLocalDeviceIdleController != null) {
+ if (mReportedSyncActive != active) {
+ mReportedSyncActive = active;
+ mLocalDeviceIdleController.setSyncActive(active);
+ }
+ }
+
+ }
+
private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
final long bytesTransferredCurrent =
getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
@@ -3114,6 +3215,7 @@
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(
new SyncOperation(info.account, info.userId,
+ syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, info.provider, new Bundle(),
0 /* delay */, 0 /* flex */,
@@ -3124,6 +3226,7 @@
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(
new SyncOperation(info.service, info.userId,
+ syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, new Bundle(),
0 /* delay */, 0 /* flex */,
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 10efe81..ab777ae 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -22,6 +22,7 @@
import android.content.ContentResolver;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
/**
@@ -62,6 +63,8 @@
/** Identifying info for the target for this operation. */
public final SyncStorageEngine.EndPoint target;
+ public final int owningUid;
+ public final String owningPackage;
/** Why this sync was kicked off. {@link #REASON_NAMES} */
public final int reason;
/** Where this sync was initiated. */
@@ -93,25 +96,28 @@
/** Whether this sync op was recently skipped due to the app being idle */
public boolean appIdle;
- public SyncOperation(Account account, int userId, int reason, int source, String provider,
- Bundle extras, long runTimeFromNow, long flexTime, long backoff,
- long delayUntil, boolean allowParallelSyncs) {
- this(new SyncStorageEngine.EndPoint(account, provider, userId),
+ public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
+ int reason, int source, String provider, Bundle extras, long runTimeFromNow,
+ long flexTime, long backoff, long delayUntil, boolean allowParallelSyncs) {
+ this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
allowParallelSyncs);
}
- public SyncOperation(ComponentName service, int userId, int reason, int source,
- Bundle extras, long runTimeFromNow, long flexTime, long backoff,
+ public SyncOperation(ComponentName service, int userId, int owningUid, String owningPackage,
+ int reason, int source, Bundle extras, long runTimeFromNow, long flexTime, long backoff,
long delayUntil) {
- this(new SyncStorageEngine.EndPoint(service, userId), reason, source, extras,
- runTimeFromNow, flexTime, backoff, delayUntil, true /* allowParallelSyncs */);
+ this(new SyncStorageEngine.EndPoint(service, userId, owningUid), owningUid, owningPackage,
+ reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
+ true /* allowParallelSyncs */);
}
- private SyncOperation(SyncStorageEngine.EndPoint info, int reason, int source, Bundle extras,
- long runTimeFromNow, long flexTime, long backoff, long delayUntil,
- boolean allowParallelSyncs) {
+ private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
+ int reason, int source, Bundle extras, long runTimeFromNow, long flexTime,
+ long backoff, long delayUntil, boolean allowParallelSyncs) {
this.target = info;
+ this.owningUid = owningUid;
+ this.owningPackage = owningPackage;
this.reason = reason;
this.syncSource = source;
this.extras = new Bundle(extras);
@@ -142,7 +148,8 @@
/** Used to reschedule a sync at a new point in time. */
public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
- this(other.target, other.reason, other.syncSource, new Bundle(other.extras),
+ this(other.target, other.owningUid, other.owningPackage, other.reason, other.syncSource,
+ new Bundle(other.extras),
newRunTimeFromNow,
0L /* In back-off so no flex */,
other.backoff,
@@ -228,6 +235,13 @@
}
sb.append(", reason: ");
sb.append(reasonToString(pm, reason));
+ if (!useOneLine) {
+ sb.append("\n ");
+ sb.append("owningUid=");
+ UserHandle.formatUid(sb, owningUid);
+ sb.append(" owningPackage=");
+ sb.append(owningPackage);
+ }
if (!useOneLine && !extras.keySet().isEmpty()) {
sb.append("\n ");
extrasToStringBuilder(extras, sb);
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 587de1c..b15d0d8 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -16,16 +16,20 @@
package com.android.server.content;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.content.pm.PackageManager;
import android.content.SyncAdapterType;
import android.content.SyncAdaptersCache;
import android.content.pm.RegisteredServicesCache.ServiceInfo;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import com.google.android.collect.Maps;
import java.util.ArrayList;
@@ -74,8 +78,9 @@
continue;
}
operationToAdd = new SyncOperation(
- info.account, info.userId, op.reason, op.syncSource, info.provider,
- op.extras,
+ info.account, info.userId, syncAdapterInfo.uid,
+ syncAdapterInfo.componentName.getPackageName(), op.reason,
+ op.syncSource, info.provider, op.extras,
op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
backoff != null ? backoff.first : 0L,
@@ -84,16 +89,24 @@
operationToAdd.pendingOperation = op;
add(operationToAdd, op);
} else if (info.target_service) {
+ android.content.pm.ServiceInfo sinfo;
try {
- mPackageManager.getServiceInfo(info.service, 0);
+ sinfo = mPackageManager.getServiceInfo(info.service, info.userId);
} catch (PackageManager.NameNotFoundException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.w(TAG, "Missing sync service for authority " + op.target);
}
continue;
}
+ if (sinfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.w(TAG, "Missing sync service for authority " + op.target);
+ }
+ continue;
+ }
operationToAdd = new SyncOperation(
- info.service, info.userId, op.reason, op.syncSource,
+ info.service, info.userId, sinfo.applicationInfo.uid,
+ info.service.getPackageName(), op.reason, op.syncSource,
op.extras,
op.expedited ? -1 : 0 /* delay */,
0 /* flex */,
@@ -161,9 +174,38 @@
opsToRemove.add(op);
}
}
- for (SyncOperation op : opsToRemove) {
- remove(op);
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
+ public boolean removeUidIfNeededLocked(int uid) {
+ ArrayList<SyncOperation> opsToRemove = null;
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.owningUid != uid) {
+ continue;
}
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(op.owningUid,
+ op.owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Removing sync " + op.owningUid + ":" + op
+ + " -- package not allowed to start");
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
+ if (opsToRemove == null) {
+ opsToRemove = new ArrayList<SyncOperation>();
+ }
+ opsToRemove.add(op);
+ }
+ if (opsToRemove == null) {
+ return false;
+ }
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index cca0c16..8266c08 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -27,6 +27,7 @@
import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -42,6 +43,7 @@
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.ArrayMap;
import android.util.Xml;
@@ -227,14 +229,16 @@
public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL =
new EndPoint(null, null, UserHandle.USER_ALL);
final ComponentName service;
+ final int serviceUid; // -1 for "any"
final Account account;
final int userId;
final String provider;
final boolean target_service;
final boolean target_provider;
- public EndPoint(ComponentName service, int userId) {
+ public EndPoint(ComponentName service, int userId, int uid) {
this.service = service;
+ this.serviceUid = uid;
this.userId = userId;
this.account = null;
this.provider = null;
@@ -247,6 +251,7 @@
this.provider = provider;
this.userId = userId;
this.service = null;
+ this.serviceUid = -1;
this.target_service = false;
this.target_provider = true;
}
@@ -264,6 +269,11 @@
return false;
}
if (target_service && spec.target_service) {
+ if (serviceUid != spec.serviceUid
+ && serviceUid >= 0
+ && spec.serviceUid >= 0) {
+ return false;
+ }
return service.equals(spec.service);
} else if (target_provider && spec.target_provider) {
boolean accountsMatch;
@@ -290,8 +300,9 @@
.append("/")
.append(provider == null ? "ALL PDRS" : provider);
} else if (target_service) {
- sb.append(service.getPackageName() + "/")
- .append(service.getClassName());
+ service.appendShortString(sb);
+ sb.append(":");
+ UserHandle.formatUid(sb,serviceUid);
} else {
sb.append("invalid target");
}
@@ -737,7 +748,7 @@
synchronized (mAuthorities) {
if (cname != null) {
AuthorityInfo authority = getAuthorityLocked(
- new EndPoint(cname, userId),
+ new EndPoint(cname, userId, -1),
"get service active");
if (authority == null) {
return false;
@@ -749,7 +760,7 @@
}
public void setIsTargetServiceActive(ComponentName cname, int userId, boolean active) {
- setSyncableStateForEndPoint(new EndPoint(cname, userId), active ?
+ setSyncableStateForEndPoint(new EndPoint(cname, userId, -1), active ?
AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE);
}
@@ -2064,18 +2075,30 @@
new Account(accountName, accountType),
authorityName, userId);
} else {
- info = new EndPoint(
- new ComponentName(packageName, className),
- userId);
+ final ComponentName cname = new ComponentName(packageName, className);
+ android.content.pm.ServiceInfo sinfo = null;
+ try {
+ sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Not restoring sync " + cname
+ + " -- can't find service for user " + userId);
+ }
+ if (sinfo != null) {
+ info = new EndPoint(cname, userId, sinfo.applicationInfo.uid);
+ } else {
+ info = null;
+ }
}
- authority = getOrCreateAuthorityLocked(info, id, false);
- // If the version is 0 then we are upgrading from a file format that did not
- // know about periodic syncs. In that case don't clear the list since we
- // want the default, which is a daily periodic sync.
- // Otherwise clear out this default list since we will populate it later with
- // the periodic sync descriptions that are read from the configuration file.
- if (version > 0) {
- authority.periodicSyncs.clear();
+ if (info != null) {
+ authority = getOrCreateAuthorityLocked(info, id, false);
+ // If the version is 0 then we are upgrading from a file format that did not
+ // know about periodic syncs. In that case don't clear the list since we
+ // want the default, which is a daily periodic sync.
+ // Otherwise clear out this default list since we will populate it later with
+ // the periodic sync descriptions that are read from the configuration file.
+ if (version > 0) {
+ authority.periodicSyncs.clear();
+ }
}
}
if (authority != null) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ec7c1c4..103ed0a 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -1139,6 +1139,12 @@
public void onUserSwitching(int newUserId, IRemoteCallback reply) {
mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
.sendToTarget();
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
}
@Override
public void onUserSwitchComplete(int newUserId) throws RemoteException {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 2eca42b..adc1cd7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -448,4 +448,20 @@
static boolean isSupportedKeycode(int androidKeycode) {
return HdmiCecKeycode.androidKeyToCecKey(androidKeycode) != null;
}
+
+ /**
+ * Returns CEC keycode to control audio mute status.
+ *
+ * @param muting {@code true} if audio is being muted
+ */
+ public static int getMuteKey(boolean muting) {
+ // CEC_KEYCODE_MUTE_FUNCTION, CEC_KEYCODE_RESTORE_VOLUME_FUNCTION are deterministic
+ // commands that ensures the status changes to what we want, while CEC_KEYCODE_MUTE
+ // simply toggles the status.
+ // The former is a better choice in this regard, but there are compatibility issues
+ // observed - many audio receivers don't recognize the commands. We fall back on
+ // CEC_KEYCODE_MUTE for now.
+ // return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
+ return CEC_KEYCODE_MUTE;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index cd8484f..e63a143 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1099,8 +1099,7 @@
// Remove existing volume action.
removeAction(VolumeControlAction.class);
sendUserControlPressedAndReleased(getAvrDeviceInfo().getLogicalAddress(),
- mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION :
- HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
+ HdmiCecKeycode.getMuteKey(mute));
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index dba3591..2ae5c97 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -68,10 +68,8 @@
// the audio amplifier is unknown.
tv().setAudioStatus(false, Constants.UNKNOWN_VOLUME);
- int uiCommand = tv().isSystemAudioActivated()
- ? HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON
- : HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION; // SystemAudioMode: OFF
- sendUserControlPressedAndReleased(mAvrAddress, uiCommand);
+ sendUserControlPressedAndReleased(mAvrAddress,
+ HdmiCecKeycode.getMuteKey(!tv().isSystemAudioActivated()));
// Still return SUCCESS to callback.
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 2b535b9..4d7df9c 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -23,7 +23,9 @@
import java.util.List;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.IUidObserver;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.app.job.JobService;
@@ -51,6 +53,8 @@
import android.util.SparseArray;
import com.android.internal.app.IBatteryStats;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
import com.android.server.job.controllers.AppIdleController;
import com.android.server.job.controllers.BatteryController;
import com.android.server.job.controllers.ConnectivityController;
@@ -83,6 +87,7 @@
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
+ static final int MSG_STOP_JOB = 2;
// Policy constants
/**
@@ -127,6 +132,7 @@
IBatteryStats mBatteryStats;
PowerManager mPowerManager;
+ DeviceIdleController.LocalService mLocalDeviceIdleController;
/**
* Set to true once we are allowed to run third party apps.
@@ -139,6 +145,11 @@
boolean mDeviceIdleMode;
/**
+ * What we last reported to DeviceIdleController about wheter we are active.
+ */
+ boolean mReportedActive;
+
+ /**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
* still clean up. On reinstall the package will have a new uid.
*/
@@ -154,7 +165,7 @@
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForUid(uidRemoved);
+ cancelJobsForUid(uidRemoved, true);
}
} else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -172,6 +183,21 @@
}
};
+ final private IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
+ }
+
+ @Override public void onUidGone(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ cancelJobsForUid(uid, false);
+ }
+ };
+
@Override
public void onStartUser(int userHandle) {
mStartedUsers.add(userHandle);
@@ -194,6 +220,15 @@
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(uId,
+ job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
+ + " -- package not allowed to start");
+ return JobScheduler.RESULT_FAILURE;
+ }
+ } catch (RemoteException e) {
+ }
startTrackingJob(jobStatus);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
@@ -229,14 +264,26 @@
* This will remove the job from the master list, and cancel the job if it was staged for
* execution or being executed.
* @param uid Uid to check against for removal of a job.
+ * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
+ * whose apps are stopped.
*/
- public void cancelJobsForUid(int uid) {
+ public void cancelJobsForUid(int uid, boolean forceAll) {
List<JobStatus> jobsForUid;
synchronized (mJobs) {
jobsForUid = mJobs.getJobsByUid(uid);
}
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
+ if (!forceAll) {
+ String packageName = toRemove.getServiceComponent().getPackageName();
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
+ != ActivityManager.APP_START_MODE_DISABLED) {
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
+ }
cancelJobImpl(toRemove);
}
}
@@ -268,6 +315,7 @@
mPendingJobs.remove(cancelled);
// Cancel if running.
stopJobOnServiceContextLocked(cancelled);
+ reportActive();
}
}
@@ -299,12 +347,39 @@
}
} else {
// When coming out of idle, allow thing to start back up.
+ if (rocking) {
+ if (mLocalDeviceIdleController != null) {
+ if (!mReportedActive) {
+ mReportedActive = true;
+ mLocalDeviceIdleController.setJobsActive(true);
+ }
+ }
+ }
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
}
}
}
+ void reportActive() {
+ boolean active = false;
+ if (mPendingJobs.size() <= 0) {
+ for (int i=0; i<mActiveServices.size(); i++) {
+ JobServiceContext jsc = mActiveServices.get(i);
+ if (!jsc.isAvailable()) {
+ active = true;
+ break;
+ }
+ }
+ }
+ if (mLocalDeviceIdleController != null) {
+ if (mReportedActive != active) {
+ mReportedActive = active;
+ mLocalDeviceIdleController.setJobsActive(active);
+ }
+ }
+ }
+
/**
* Initializes the system service.
* <p>
@@ -348,12 +423,20 @@
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
+ try {
+ ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_IDLE);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mJobs) {
// Let's go!
mReadyToRock = true;
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
+ mLocalDeviceIdleController
+ = LocalServices.getService(DeviceIdleController.LocalService.class);
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
mActiveServices.add(
@@ -597,6 +680,9 @@
maybeQueueReadyJobsForExecutionLockedH();
}
break;
+ case MSG_STOP_JOB:
+ cancelJobImpl((JobStatus)message.obj);
+ break;
}
maybeRunPendingJobsH();
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
@@ -623,6 +709,7 @@
stopJobOnServiceContextLocked(job);
}
}
+ reportActive();
if (DEBUG) {
final int queuedJobs = mPendingJobs.size();
if (queuedJobs == 0) {
@@ -647,11 +734,22 @@
int idleCount = 0;
int backoffCount = 0;
int connectivityCount = 0;
- List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
+ List<JobStatus> runnableJobs = null;
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (isReadyToBeExecutedLocked(job)) {
+ try {
+ if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
+ job.getJob().getService().getPackageName())
+ == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Aborting job " + job.getUid() + ":"
+ + job.getJob().toString() + " -- package not allowed to start");
+ mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
if (job.getNumFailures() > 0) {
backoffCount++;
}
@@ -664,6 +762,9 @@
if (job.hasChargingConstraint()) {
chargingCount++;
}
+ if (runnableJobs == null) {
+ runnableJobs = new ArrayList<>();
+ }
runnableJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
stopJobOnServiceContextLocked(job);
@@ -673,7 +774,7 @@
idleCount >= MIN_IDLE_COUNT ||
connectivityCount >= MIN_CONNECTIVITY_COUNT ||
chargingCount >= MIN_CHARGING_COUNT ||
- runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
+ (runnableJobs != null && runnableJobs.size() >= MIN_READY_JOBS_COUNT)) {
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
}
@@ -685,6 +786,7 @@
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
}
}
+ reportActive();
if (DEBUG) {
Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
connectivityCount + " charging=" + chargingCount + " tot=" +
@@ -766,6 +868,7 @@
it.remove();
}
}
+ reportActive();
}
}
}
@@ -867,7 +970,7 @@
long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJobsForUid(uid);
+ JobSchedulerService.this.cancelJobsForUid(uid, true);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -948,6 +1051,7 @@
pw.println();
pw.print("mReadyToRock="); pw.println(mReadyToRock);
pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
+ pw.print("mReportedActive="); pw.println(mReportedActive);
}
pw.println();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 7028fa6..90dd10ea 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -948,8 +948,8 @@
// Launch the last PendingIntent we had with priority
int userId = ActivityManager.getCurrentUser();
UserRecord user = mUserRecords.get(userId);
- if (user.mLastMediaButtonReceiver != null
- || user.mRestoredMediaButtonReceiver != null) {
+ if (user != null && (user.mLastMediaButtonReceiver != null
+ || user.mRestoredMediaButtonReceiver != null)) {
if (DEBUG) {
Log.d(TAG, "Sending media key to last known PendingIntent "
+ user.mLastMediaButtonReceiver + " or restored Intent "
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 9db6a06..5b1cedc 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -239,9 +239,7 @@
throw new RuntimeException("Problem setting firewall rules", e);
}
- synchronized (mStateLock) {
- handleStateChangedLocked();
- }
+ handleStateChangedLocked();
}
public void shutdown() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 41aea04..2ac0ba6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -459,7 +459,8 @@
updateScreenOn();
try {
- mActivityManager.registerUidObserver(mUidObserver);
+ mActivityManager.registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE);
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
@@ -541,6 +542,12 @@
removeUidStateLocked(uid);
}
}
+
+ @Override public void onUidActive(int uid) throws RemoteException {
+ }
+
+ @Override public void onUidIdle(int uid) throws RemoteException {
+ }
};
final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b84811f..946fbb1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
import static android.service.notification.NotificationListenerService.TRIM_FULL;
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -78,7 +80,6 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.Condition;
-import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
@@ -1210,42 +1211,36 @@
}
@Override
- public void setPackagePriority(String pkg, int uid, int priority) {
+ public ParceledListSlice<Notification.Topic> getTopics(String pkg, int uid) {
checkCallerIsSystem();
- mRankingHelper.setPackagePriority(pkg, uid, priority);
+ return new ParceledListSlice<Notification.Topic>(mRankingHelper.getTopics(pkg, uid));
+ }
+
+ @Override
+ public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
+ checkCallerIsSystem();
+ mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
savePolicyFile();
}
@Override
- public int getPackagePriority(String pkg, int uid) {
+ public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getPackagePriority(pkg, uid);
+ return mRankingHelper.getTopicPriority(pkg, uid, topic);
}
@Override
- public void setPackagePeekable(String pkg, int uid, boolean peekable) {
+ public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
+ int visibility) {
checkCallerIsSystem();
-
- mRankingHelper.setPackagePeekable(pkg, uid, peekable);
- }
-
- @Override
- public boolean getPackagePeekable(String pkg, int uid) {
- checkCallerIsSystem();
- return mRankingHelper.getPackagePeekable(pkg, uid);
- }
-
- @Override
- public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
- checkCallerIsSystem();
- mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
+ mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
savePolicyFile();
}
@Override
- public int getPackageVisibilityOverride(String pkg, int uid) {
+ public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
- return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
+ return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
}
/**
@@ -2156,14 +2151,6 @@
notification.priority = Notification.PRIORITY_HIGH;
}
}
- // force no heads up per package config
- if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
- if (notification.extras == null) {
- notification.extras = new Bundle();
- }
- notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
- Notification.HEADS_UP_NEVER);
- }
// 1. initial score: buckets of 10, around the app [-20..20]
final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
@@ -2511,7 +2498,9 @@
// light
// release the light
boolean wasShowLights = mLights.remove(record.getKey());
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
+ && ((record.getSuppressedVisualEffects()
+ & NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS) == 0)) {
mLights.add(record.getKey());
updateLightsLocked();
if (mUseAttentionLight) {
@@ -2701,6 +2690,11 @@
// let zen mode evaluate this record
private void applyZenModeLocked(NotificationRecord record) {
record.setIntercepted(mZenModeHelper.shouldIntercept(record));
+ if (record.isIntercepted()) {
+ int suppressed = (mZenModeHelper.shouldSuppressLight() ? SUPPRESSED_EFFECT_LIGHTS : 0)
+ | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0);
+ record.setSuppressedVisualEffects(suppressed);
+ }
}
// lock on mNotificationList
@@ -3234,6 +3228,7 @@
ArrayList<String> keys = new ArrayList<String>(N);
ArrayList<String> interceptedKeys = new ArrayList<String>(N);
Bundle visibilityOverrides = new Bundle();
+ Bundle suppressedVisualEffects = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -3242,7 +3237,10 @@
keys.add(record.sbn.getKey());
if (record.isIntercepted()) {
interceptedKeys.add(record.sbn.getKey());
+
}
+ suppressedVisualEffects.putInt(
+ record.sbn.getKey(), record.getSuppressedVisualEffects());
if (record.getPackageVisibilityOverride()
!= NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
visibilityOverrides.putInt(record.sbn.getKey(),
@@ -3264,7 +3262,7 @@
String[] keysAr = keys.toArray(new String[keys.size()]);
String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
- speedBumpIndex);
+ speedBumpIndex, suppressedVisualEffects);
}
private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f37702c..2a7568d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -83,6 +83,8 @@
private String mGlobalSortKey;
private int mPackageVisibility;
+ private int mSuppressedVisualEffects = 0;
+
@VisibleForTesting
public NotificationRecord(StatusBarNotification sbn, int score)
{
@@ -199,6 +201,7 @@
pw.println(prefix + " mCreationTimeMs=" + mCreationTimeMs);
pw.println(prefix + " mVisibleSinceMs=" + mVisibleSinceMs);
pw.println(prefix + " mUpdateTimeMs=" + mUpdateTimeMs);
+ pw.println(prefix + " mSuppressedVisualEffects= " + mSuppressedVisualEffects);
}
@@ -274,6 +277,14 @@
return mIntercept;
}
+ public void setSuppressedVisualEffects(int effects) {
+ mSuppressedVisualEffects = effects;
+ }
+
+ public int getSuppressedVisualEffects() {
+ return mSuppressedVisualEffects;
+ }
+
public boolean isCategory(String category) {
return Objects.equals(getNotification().category, category);
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 803db10..7ee29e4 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -15,16 +15,20 @@
*/
package com.android.server.notification;
+import android.app.Notification;
+
+import java.util.List;
+
public interface RankingConfig {
- int getPackagePriority(String packageName, int uid);
- void setPackagePriority(String packageName, int uid, int priority);
+ List<Notification.Topic> getTopics(String packageName, int uid);
- boolean getPackagePeekable(String packageName, int uid);
+ int getTopicPriority(String packageName, int uid, Notification.Topic topic);
- void setPackagePeekable(String packageName, int uid, boolean peekable);
+ void setTopicPriority(String packageName, int uid, Notification.Topic topic, int priority);
- int getPackageVisibilityOverride(String packageName, int uid);
+ int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic);
- void setPackageVisibilityOverride(String packageName, int uid, int visibility);
+ void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic,
+ int visibility);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 66381f5..543cd89 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -27,6 +27,8 @@
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -35,6 +37,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RankingHelper implements RankingConfig {
@@ -45,15 +49,16 @@
private static final String TAG_RANKING = "ranking";
private static final String TAG_PACKAGE = "package";
private static final String ATT_VERSION = "version";
+ private static final String TAG_TOPIC = "topic";
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
- private static final String ATT_PEEKABLE = "peekable";
private static final String ATT_VISIBILITY = "visibility";
+ private static final String ATT_TOPIC_ID = "id";
+ private static final String ATT_TOPIC_LABEL = "label";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
- private static final boolean DEFAULT_PEEKABLE = true;
private static final int DEFAULT_VISIBILITY =
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
@@ -141,7 +146,6 @@
if (TAG_PACKAGE.equals(tag)) {
int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID);
int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
- boolean peekable = safeBool(parser, ATT_PEEKABLE, DEFAULT_PEEKABLE);
int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
String name = parser.getAttributeValue(null, ATT_NAME);
@@ -164,15 +168,14 @@
} else {
r = getOrCreateRecord(name, uid);
}
- if (priority != DEFAULT_PRIORITY) {
- r.priority = priority;
- }
- if (peekable != DEFAULT_PEEKABLE) {
- r.peekable = peekable;
- }
- if (vis != DEFAULT_VISIBILITY) {
- r.visibility = vis;
- }
+
+ // Migrate package level settings to the default topic.
+ // Might be overwritten by parseTopics.
+ Topic defaultTopic = r.topics.get(Notification.TOPIC_DEFAULT);
+ defaultTopic.priority = priority;
+ defaultTopic.visibility = vis;
+
+ parseTopics(r, parser);
}
}
}
@@ -180,6 +183,38 @@
throw new IllegalStateException("Failed to reach END_DOCUMENT");
}
+ public void parseTopics(Record r, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int innerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (TAG_TOPIC.equals(tagName)) {
+ int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+ int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ String id = parser.getAttributeValue(null, ATT_TOPIC_ID);
+ CharSequence label = parser.getAttributeValue(null, ATT_TOPIC_LABEL);
+
+ if (!TextUtils.isEmpty(id)) {
+ Topic topic = new Topic(new Notification.Topic(id, label));
+
+ if (priority != DEFAULT_PRIORITY) {
+ topic.priority = priority;
+ }
+ if (vis != DEFAULT_VISIBILITY) {
+ topic.visibility = vis;
+ }
+ r.topics.put(id, topic);
+ }
+ }
+ }
+ }
+
private static String recordKey(String pkg, int uid) {
return pkg + "|" + uid;
}
@@ -191,22 +226,12 @@
r = new Record();
r.pkg = pkg;
r.uid = uid;
+ r.topics.put(Notification.TOPIC_DEFAULT, new Topic(createDefaultTopic()));
mRecords.put(key, r);
}
return r;
}
- private void removeDefaultRecords() {
- final int N = mRecords.size();
- for (int i = N - 1; i >= 0; i--) {
- final Record r = mRecords.valueAt(i);
- if (r.priority == DEFAULT_PRIORITY && r.peekable == DEFAULT_PEEKABLE
- && r.visibility == DEFAULT_VISIBILITY) {
- mRecords.remove(i);
- }
- }
- }
-
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, TAG_RANKING);
out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
@@ -220,23 +245,32 @@
}
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
- if (r.priority != DEFAULT_PRIORITY) {
- out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
- }
- if (r.peekable != DEFAULT_PEEKABLE) {
- out.attribute(null, ATT_PEEKABLE, Boolean.toString(r.peekable));
- }
- 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));
}
+
+ writeTopicsXml(out, r);
out.endTag(null, TAG_PACKAGE);
}
out.endTag(null, TAG_RANKING);
}
+ public void writeTopicsXml(XmlSerializer out, Record r) throws IOException {
+ for (Topic t : r.topics.values()) {
+ out.startTag(null, TAG_TOPIC);
+ out.attribute(null, ATT_TOPIC_ID, t.topic.getId());
+ out.attribute(null, ATT_TOPIC_LABEL, t.topic.getLabel().toString());
+ if (t.priority != DEFAULT_PRIORITY) {
+ out.attribute(null, ATT_PRIORITY, Integer.toString(t.priority));
+ }
+ if (t.visibility != DEFAULT_VISIBILITY) {
+ out.attribute(null, ATT_VISIBILITY, Integer.toString(t.visibility));
+ }
+ out.endTag(null, TAG_TOPIC);
+ }
+ }
+
private void updateConfig() {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
@@ -332,51 +366,60 @@
}
@Override
- public int getPackagePriority(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.priority : DEFAULT_PRIORITY;
+ 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()) {
+ topics.add(t.topic);
+ }
+ return topics;
}
@Override
- public void setPackagePriority(String packageName, int uid, int priority) {
- if (priority == getPackagePriority(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).priority = priority;
- removeDefaultRecords();
+ public int getTopicPriority(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ return getOrCreateTopic(r, topic).priority;
+ }
+
+ @Override
+ public void setTopicPriority(String packageName, int uid, Notification.Topic topic,
+ int priority) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ getOrCreateTopic(r, topic).priority = priority;
updateConfig();
}
@Override
- public boolean getPackagePeekable(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.peekable : DEFAULT_PEEKABLE;
+ public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
+ final Record r = getOrCreateRecord(packageName, uid);
+ return getOrCreateTopic(r, topic).visibility;
}
@Override
- public void setPackagePeekable(String packageName, int uid, boolean peekable) {
- if (peekable == getPackagePeekable(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).peekable = peekable;
- removeDefaultRecords();
+ 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 getPackageVisibilityOverride(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.visibility : DEFAULT_VISIBILITY;
+ private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
+ if (topic == null) {
+ topic = createDefaultTopic();
+ }
+ Topic t = r.topics.get(topic.getId());
+ if (t != null) {
+ return t;
+ } else {
+ t = new Topic(topic);
+ r.topics.put(topic.getId(), t);
+ return t;
+ }
}
- @Override
- public void setPackageVisibilityOverride(String packageName, int uid, int visibility) {
- if (visibility == getPackageVisibilityOverride(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).visibility = visibility;
- removeDefaultRecords();
- updateConfig();
+ private Notification.Topic createDefaultTopic() {
+ return new Notification.Topic(Notification.TOPIC_DEFAULT,
+ mContext.getString(R.string.default_notification_topic_label));
}
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
@@ -411,19 +454,22 @@
pw.print(" (");
pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
- if (r.priority != DEFAULT_PRIORITY) {
- pw.print(" priority=");
- pw.print(Notification.priorityToString(r.priority));
- }
- if (r.peekable != DEFAULT_PEEKABLE) {
- pw.print(" peekable=");
- pw.print(r.peekable);
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- pw.print(" visibility=");
- pw.print(Notification.visibilityToString(r.visibility));
- }
pw.println();
+ for (Topic t : r.topics.values()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(" ");
+ pw.print(t.topic.getId());
+ if (t.priority != DEFAULT_PRIORITY) {
+ pw.print(" priority=");
+ pw.print(Notification.priorityToString(t.priority));
+ }
+ if (t.visibility != DEFAULT_VISIBILITY) {
+ pw.print(" visibility=");
+ pw.print(Notification.visibilityToString(t.visibility));
+ }
+ pw.println();
+ }
}
}
}
@@ -459,9 +505,16 @@
String pkg;
int uid = UNKNOWN_UID;
- int priority = DEFAULT_PRIORITY;
- boolean peekable = DEFAULT_PEEKABLE;
- int visibility = DEFAULT_VISIBILITY;
- }
+ Map<String, Topic> topics = new ArrayMap<>();
+ }
+ private static class Topic {
+ Notification.Topic topic;
+ int priority = DEFAULT_PRIORITY;
+ int visibility = DEFAULT_VISIBILITY;
+
+ public Topic(Notification.Topic topic) {
+ this.topic = topic;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
similarity index 79%
rename from services/core/java/com/android/server/notification/PackagePriorityExtractor.java
rename to services/core/java/com/android/server/notification/TopicPriorityExtractor.java
index 6beed9c..5bf989ae 100644
--- a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.util.Slog;
-public class PackagePriorityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "ImportantPackageExtractor";
+/**
+ * Determines if the given notification can bypass Do Not Disturb.
+ */
+public class TopicPriorityExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "ImportantTopicExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
@@ -39,8 +42,8 @@
return null;
}
- final int packagePriority = mConfig.getPackagePriority(
- record.sbn.getPackageName(), record.sbn.getUid());
+ final int packagePriority = mConfig.getTopicPriority(record.sbn.getPackageName(),
+ record.sbn.getUid(), record.sbn.getNotification().getTopic());
record.setPackagePriority(packagePriority);
return null;
diff --git a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
similarity index 80%
rename from services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
rename to services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
index af99db7..e053382 100644
--- a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.util.Slog;
-public class PackageVisibilityExtractor implements NotificationSignalExtractor {
- private static final String TAG = "PackageVisibilityExtractor";
+/**
+ * Determines if the given notification can display sensitive content on the lockscreen.
+ */
+public class TopicVisibilityExtractor implements NotificationSignalExtractor {
+ private static final String TAG = "TopicVisibilityExtractor";
private static final boolean DBG = false;
private RankingConfig mConfig;
@@ -39,8 +42,9 @@
return null;
}
- final int packageVisibility = mConfig.getPackageVisibilityOverride(
- record.sbn.getPackageName(), record.sbn.getUid());
+ final int packageVisibility = mConfig.getTopicVisibilityOverride(
+ record.sbn.getPackageName(), record.sbn.getUid(),
+ record.sbn.getNotification().getTopic());
record.setPackageVisibilityOverride(packageVisibility);
return null;
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 0420269..c9e1315 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -41,6 +41,9 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import android.os.SystemClock;
+import com.android.internal.logging.MetricsLogger;
+
/**
* This {@link NotificationSignalExtractor} attempts to validate
* people references. Also elevates the priority of real people.
@@ -218,6 +221,7 @@
private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
float[] affinityOut) {
+ long start = SystemClock.elapsedRealtime();
float affinity = NONE;
if (extras == null) {
return null;
@@ -251,6 +255,9 @@
// record the best available data, so far:
affinityOut[0] = affinity;
+ MetricsLogger.histogram(mBaseContext, "validate_people_cache_latency",
+ (int) (SystemClock.elapsedRealtime() - start));
+
if (pendingLookups.isEmpty()) {
if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity);
return null;
@@ -430,6 +437,7 @@
@Override
public void work() {
+ long start = SystemClock.elapsedRealtime();
if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
long timeStartMs = System.currentTimeMillis();
for (final String handle: mPendingLookups) {
@@ -468,6 +476,9 @@
mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE,
mContactAffinity == STARRED_CONTACT, false /* cached */);
}
+
+ MetricsLogger.histogram(mBaseContext, "validate_people_lookup_latency",
+ (int) (SystemClock.elapsedRealtime() - start));
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a1f8c41..dbdc3f4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -138,6 +138,18 @@
}
}
+ public boolean shouldSuppressLight() {
+ synchronized (mConfig) {
+ return !mConfig.allowLights;
+ }
+ }
+
+ public boolean shouldSuppressPeek() {
+ synchronized (mConfig) {
+ return !mConfig.allowPeek;
+ }
+ }
+
public void addCallback(Callback callback) {
mCallbacks.add(callback);
}
@@ -394,11 +406,11 @@
return;
}
pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s,"
- + "events=%s,reminders=%s)\n",
+ + "events=%s,reminders=%s,lights=%s,peek=%s)\n",
config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
config.allowRepeatCallers, config.allowMessages,
ZenModeConfig.sourceToString(config.allowMessagesFrom),
- config.allowEvents, config.allowReminders);
+ config.allowEvents, config.allowReminders, config.allowLights, config.allowPeek);
pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
if (config.automaticRules.isEmpty()) return;
final int N = config.automaticRules.size();
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 7b1ac5ca..af20679 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -28,6 +28,7 @@
import android.util.Log;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
/**
* {@hide}
@@ -55,6 +56,7 @@
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setMinimumLatency(minLatency)
+ .setPeriodic(TimeUnit.DAYS.toMillis(1))
.build();
js.schedule(job);
}
@@ -89,7 +91,7 @@
// skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, null /* instruction set */, true)) {
+ if (!pm.performDexOpt(pkg, null /* instruction set */)) {
// there was a problem running dexopt,
// remember this so we do not keep retrying.
sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index e4dbf65..073b4f03 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -57,6 +57,8 @@
private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
private static final boolean DEBUG = false;
+ private static final int DEFAULT_FLAGS = PackageManager.GET_ENCRYPTION_UNAWARE_COMPONENTS;
+
private static final String AUDIO_MIME_TYPE = "audio/mpeg";
private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
@@ -696,7 +698,7 @@
private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
Intent intent, int userId) {
ResolveInfo handler = mService.resolveIntent(intent,
- intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
if (handler == null || handler.activityInfo == null) {
return null;
}
@@ -711,7 +713,7 @@
private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
Intent intent, int userId) {
List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
if (handlers == null) {
return null;
}
@@ -738,7 +740,8 @@
homeIntent.setPackage(syncAdapterPackageName);
ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ homeIntent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS,
+ userId);
if (homeActivity != null) {
continue;
}
@@ -754,7 +757,7 @@
private PackageParser.Package getDefaultProviderAuthorityPackageLPr(
String authority, int userId) {
- ProviderInfo provider = mService.resolveContentProvider(authority, 0, userId);
+ ProviderInfo provider = mService.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
if (provider != null) {
return getSystemPackageLPr(provider.packageName);
}
@@ -871,7 +874,7 @@
return false;
}
PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPkg != null) {
+ if (sysPkg != null && sysPkg.pkg != null) {
if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
new file mode 100644
index 0000000..628ad0e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.TimedRemoteCaller;
+
+import com.android.internal.app.EphemeralResolverService;
+import com.android.internal.app.EphemeralResolveInfo;
+import com.android.internal.app.IEphemeralResolver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Represents a remote ephemeral resolver. It is responsible for binding to the remote
+ * service and handling all interactions in a timely manner.
+ * @hide
+ */
+final class EphemeralResolverConnection {
+ // This is running in a critical section and the timeout must be sufficiently low
+ private static final long BIND_SERVICE_TIMEOUT_MS =
+ ("eng".equals(Build.TYPE)) ? 300 : 200;
+
+ private final Object mLock = new Object();
+ private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
+ new GetEphemeralResolveInfoCaller();
+ private final ServiceConnection mServiceConnection = new MyServiceConnection();
+ private final Context mContext;
+ /** Intent used to bind to the service */
+ private final Intent mIntent;
+
+ private IEphemeralResolver mRemoteInstance;
+
+ public EphemeralResolverConnection(Context context, ComponentName componentName) {
+ mContext = context;
+ mIntent = new Intent().setComponent(componentName);
+ }
+
+ public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix) {
+ throwIfCalledOnMainThread();
+ try {
+ return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
+ getRemoteInstanceLazy(), hashPrefix);
+ } catch (RemoteException re) {
+ } catch (TimeoutException te) {
+ } finally {
+ synchronized (mLock) {
+ mLock.notifyAll();
+ }
+ }
+ return null;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ pw.append(prefix).append("bound=")
+ .append((mRemoteInstance != null) ? "true" : "false").println();
+
+ pw.flush();
+
+ try {
+ getRemoteInstanceLazy().asBinder().dump(fd, new String[] { prefix });
+ } catch (TimeoutException te) {
+ /* ignore */
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ }
+
+ private IEphemeralResolver getRemoteInstanceLazy() throws TimeoutException {
+ synchronized (mLock) {
+ if (mRemoteInstance != null) {
+ return mRemoteInstance;
+ }
+ bindLocked();
+ return mRemoteInstance;
+ }
+ }
+
+ private void bindLocked() throws TimeoutException {
+ if (mRemoteInstance != null) {
+ return;
+ }
+
+ mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM);
+
+ final long startMillis = SystemClock.uptimeMillis();
+ while (true) {
+ if (mRemoteInstance != null) {
+ break;
+ }
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long remainingMillis = BIND_SERVICE_TIMEOUT_MS - elapsedMillis;
+ if (remainingMillis <= 0) {
+ throw new TimeoutException("Didn't bind to resolver in time.");
+ }
+ try {
+ mLock.wait(remainingMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+
+ mLock.notifyAll();
+ }
+
+ private void throwIfCalledOnMainThread() {
+ if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
+ throw new RuntimeException("Cannot invoke on the main thread");
+ }
+ }
+
+ private final class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mRemoteInstance = IEphemeralResolver.Stub.asInterface(service);
+ mLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteInstance = null;
+ }
+ }
+ }
+
+ private static final class GetEphemeralResolveInfoCaller
+ extends TimedRemoteCaller<List<EphemeralResolveInfo>> {
+ private final IRemoteCallback mCallback;
+
+ public GetEphemeralResolveInfoCaller() {
+ super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mCallback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ final ArrayList<EphemeralResolveInfo> resolveList =
+ data.getParcelableArrayList(
+ EphemeralResolverService.EXTRA_RESOLVE_INFO);
+ int sequence =
+ data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1);
+ onRemoteMethodResult(resolveList, sequence);
+ }
+ };
+ }
+
+ public List<EphemeralResolveInfo> getEphemeralResolveInfoList(
+ IEphemeralResolver target, int hashPrefix)
+ throws RemoteException, TimeoutException {
+ final int sequence = onBeforeRemoteCall();
+ target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence);
+ return getResultTimed(sequence);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 150c849..99a051a 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -185,15 +185,6 @@
return mInstaller.execute(builder.toString());
}
- public int rename(String oldname, String newname) {
- StringBuilder builder = new StringBuilder("rename");
- builder.append(' ');
- builder.append(oldname);
- builder.append(' ');
- builder.append(newname);
- return mInstaller.execute(builder.toString());
- }
-
@Deprecated
public int fixUid(String name, int uid, int gid) {
return fixUid(null, name, uid, gid);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 4582828..0796811 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -26,6 +26,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
@@ -187,11 +188,11 @@
}
@Override
- public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
+ public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
throws RemoteException {
ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
if (!isUserEnabled(user)) {
- return new ArrayList<ResolveInfo>();
+ return null;
}
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -201,7 +202,7 @@
try {
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,
user.getIdentifier());
- return apps;
+ return new ParceledListSlice<>(apps);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 56c4fb1..d29a623 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -55,7 +55,6 @@
static final int DEX_OPT_FAILED = -1;
private final PackageManagerService mPackageManagerService;
- private ArraySet<PackageParser.Package> mDeferredDexOpt;
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
@@ -75,8 +74,7 @@
* {@link PackageManagerService#mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
- boolean forceDex, boolean defer, boolean inclDependencies,
- boolean bootComplete, boolean useJit) {
+ boolean inclDependencies) {
ArraySet<String> done;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new ArraySet<String>();
@@ -91,8 +89,7 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete,
- useJit, done);
+ return performDexOptLI(pkg, instructionSets, done);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -102,7 +99,6 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
ArraySet<String> done) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -110,12 +106,10 @@
if (done != null) {
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
- performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
- bootComplete, useJit, done);
+ performDexOptLibsLI(pkg.usesLibraries, instructionSets, done);
}
if (pkg.usesOptionalLibraries != null) {
- performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
- bootComplete, useJit, done);
+ performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, done);
}
}
@@ -134,30 +128,18 @@
// 3.) we are skipping an unneeded dexopt
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
+ if (pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
continue;
}
for (String path : paths) {
final int dexoptNeeded;
- if (forceDex) {
- dexoptNeeded = DexFile.DEX2OAT_NEEDED;
- } else {
- try {
- dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
- dexCodeInstructionSet, defer);
- } catch (IOException ioe) {
- Slog.w(TAG, "IOException reading apk: " + path, ioe);
- return DEX_OPT_FAILED;
- }
- }
-
- if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- // We're deciding to defer a needed dexopt. Don't bother dexopting for other
- // paths and instruction sets. We'll deal with them all together when we process
- // our list of deferred dexopts.
- addPackageForDeferredDexopt(pkg);
- return DEX_OPT_DEFERRED;
+ try {
+ dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
+ dexCodeInstructionSet, /* defer */false);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "IOException reading apk: " + path, ioe);
+ return DEX_OPT_FAILED;
}
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
@@ -177,20 +159,17 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " oatDir = " + oatDir + " bootComplete=" + bootComplete
- + " useJit=" + useJit);
+ + " oatDir = " + oatDir);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final int dexFlags =
(!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0)
- | (useJit ? DEXOPT_USEJIT : 0);
+ | DEXOPT_BOOTCOMPLETE;
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
- // Dex2oat might fail due to compiler / verifier errors. We soldier on
- // regardless, and attempt to interpret the app as a safety net.
+ // Dex2oat might fail due to compiler / verifier errors.
if (ret == 0) {
performedDexOpt = true;
}
@@ -243,34 +222,16 @@
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
- boolean forceDex, boolean defer, boolean bootComplete, boolean useJit,
ArraySet<String> done) {
for (String libName : libs) {
PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
libName);
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, instructionSets, forceDex, defer, bootComplete, useJit, done);
+ performDexOptLI(libPkg, instructionSets, done);
}
}
}
- /**
- * Clears set of deferred dexopt packages.
- * @return content of dexopt set if it was not empty
- */
- public ArraySet<PackageParser.Package> clearDeferredDexOptPackages() {
- ArraySet<PackageParser.Package> result = mDeferredDexOpt;
- mDeferredDexOpt = null;
- return result;
- }
-
- public void addPackageForDeferredDexopt(PackageParser.Package pkg) {
- if (mDeferredDexOpt == null) {
- mDeferredDexOpt = new ArraySet<>();
- }
- mDeferredDexOpt.add(pkg);
- }
-
void systemReady() {
mSystemReady = true;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cf09b84..5d1906c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -866,10 +866,11 @@
mAppOps.checkPackage(callingUid, callerPackageName);
}
- // Check whether the caller is device owner
+ // Check whether the caller is device owner, in which case we do it silently.
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(callerPackageName);
+ boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+ callerPackageName);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, packageName, isDeviceOwner, userId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b0e43a5..fa0aa37 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -228,7 +228,8 @@
final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
final boolean forcePermissionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
- mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
+ mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+ installerPackageName);
if ((isPermissionGranted
|| isInstallerRoot
|| mIsInstallerDeviceOwner)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d7664ab..4bc79cb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,6 +105,7 @@
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.AppsQueryHelper;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
@@ -208,6 +209,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.EphemeralResolveInfo;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
@@ -251,6 +253,7 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
@@ -300,6 +303,7 @@
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
+ private static final boolean DEBUG_EPHEMERAL = false;
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
@@ -429,8 +433,6 @@
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
- final boolean mLazyDexOpt;
- final long mDexOptLRUThresholdInMills;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
@@ -599,12 +601,25 @@
private final ComponentName mIntentFilterVerifierComponent;
private int mIntentFilterVerificationToken = 0;
+ /** Component that knows whether or not an ephemeral application exists */
+ final ComponentName mEphemeralResolverComponent;
+ /** The service connection to the ephemeral resolver */
+ final EphemeralResolverConnection mEphemeralResolverConnection;
+
+ /** Component used to install ephemeral applications */
+ final ComponentName mEphemeralInstallerComponent;
+ final ActivityInfo mEphemeralInstallerActivity = new ActivityInfo();
+ final ResolveInfo mEphemeralInstallerInfo = new ResolveInfo();
+
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
= new SparseArray<IntentFilterVerificationState>();
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy =
new DefaultPermissionGrantPolicy(this);
+ // List of packages names to keep cached, even if they are uninstalled for all users
+ private List<String> mKeepUninstalledPackages;
+
private static class IFVerificationParams {
PackageParser.Package pkg;
boolean replacing;
@@ -1376,7 +1391,9 @@
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install.
if ((args.installFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
+ & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
+ && res.pkg.applicationInfo.targetSdkVersion
+ >= Build.VERSION_CODES.M) {
grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
args.installGrantPermissions);
}
@@ -1415,18 +1432,18 @@
}
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, firstUsers);
+ packageName, extras, 0, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, updateUsers);
+ packageName, extras, 0, null, null, updateUsers);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, null, null, updateUsers);
+ packageName, extras, 0, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null, null, packageName, null, updateUsers);
+ null, null, 0, packageName, null, updateUsers);
// treat asec-hosted packages like removable media on upgrade
if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
@@ -1812,10 +1829,48 @@
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
+ m.enableSystemUserApps();
ServiceManager.addService("package", m);
return m;
}
+ private void enableSystemUserApps() {
+ if (!UserManager.isSplitSystemUser()) {
+ return;
+ }
+ // For system user, enable apps based on the following conditions:
+ // - app is whitelisted or belong to one of these groups:
+ // -- system app which has no launcher icons
+ // -- system app which has INTERACT_ACROSS_USERS permission
+ // -- system IME app
+ // - app is not in the blacklist
+ AppsQueryHelper queryHelper = new AppsQueryHelper(this);
+ Set<String> enableApps = new ArraySet<>();
+ enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
+ | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
+ | AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));
+ ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
+ enableApps.addAll(wlApps);
+ ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
+ enableApps.removeAll(blApps);
+
+ List<String> systemApps = queryHelper.queryApps(0, /* systemAppsOnly */ true,
+ UserHandle.SYSTEM);
+ final int systemAppsSize = systemApps.size();
+ synchronized (mPackages) {
+ for (int i = 0; i < systemAppsSize; i++) {
+ String pName = systemApps.get(i);
+ PackageSetting pkgSetting = mSettings.mPackages.get(pName);
+ // Should not happen, but we shouldn't be failing if it does
+ if (pkgSetting == null) {
+ continue;
+ }
+ boolean installed = enableApps.contains(pName);
+ pkgSetting.setInstalled(installed, UserHandle.USER_SYSTEM);
+ }
+ }
+ }
+
static String[] splitString(String str, char sep) {
int count = 1;
int i = 0;
@@ -1856,7 +1911,6 @@
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
@@ -1872,15 +1926,6 @@
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- // TODO: add a property to control this?
- long dexOptLRUThresholdInMinutes;
- if (mLazyDexOpt) {
- dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
- } else {
- dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
- }
- mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
-
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
@@ -1929,8 +1974,7 @@
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = new UserManagerService(context, this,
- mInstallLock, mPackages);
+ sUserManager = new UserManagerService(context, this, mPackages);
// Propagate permission configuration in to package manager.
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
@@ -1975,31 +2019,14 @@
// scanning install directories.
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
- final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
-
- /**
- * Add everything in the in the boot class path to the
- * list of process files because dexopt will have been run
- * if necessary during zygote startup.
- */
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
- if (bootClassPath != null) {
- String[] bootClassPathElements = splitString(bootClassPath, ':');
- for (String element : bootClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
- if (systemServerClassPath != null) {
- String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
- for (String element : systemServerClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
+ if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
@@ -2026,7 +2053,6 @@
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
}
@@ -2042,52 +2068,6 @@
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
- // Gross hack for now: we know this file doesn't contain any
- // code, so don't dexopt it to avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
-
- // Gross hack for now: we know this file is only part of
- // the boot class path for art, so don't dexopt it to
- // avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
-
- /**
- * There are a number of commands implemented in Java, which
- * we currently need to do the dexopt on so that they can be
- * run from a non-root shell.
- */
- String[] frameworkFiles = frameworkDir.list();
- if (frameworkFiles != null) {
- // TODO: We could compile these only for the most preferred ABI. We should
- // first double check that the dex files for these commands are not referenced
- // by other system apps.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(frameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we already did it.
- if (alreadyDexOpted.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
- if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, dexCodeInstructionSet,
- dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/);
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
- }
- }
- }
- }
-
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime
@@ -2306,7 +2286,6 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
- false /* force dexopt */, false /* defer dexopt */,
false /* boot complete */);
}
@@ -2383,6 +2362,33 @@
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
+ final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
+ final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr();
+ // both the installer and resolver must be present to enable ephemeral
+ if (ephemeralInstallerComponent != null && ephemeralResolverComponent != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.i(TAG, "Ephemeral activated; resolver: " + ephemeralResolverComponent
+ + " installer:" + ephemeralInstallerComponent);
+ }
+ mEphemeralResolverComponent = ephemeralResolverComponent;
+ mEphemeralInstallerComponent = ephemeralInstallerComponent;
+ setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
+ mEphemeralResolverConnection =
+ new EphemeralResolverConnection(mContext, mEphemeralResolverComponent);
+ } else {
+ if (DEBUG_EPHEMERAL) {
+ final String missingComponent =
+ (ephemeralResolverComponent == null)
+ ? (ephemeralInstallerComponent == null)
+ ? "resolver and installer"
+ : "resolver"
+ : "installer";
+ Slog.i(TAG, "Ephemeral deactivated; missing " + missingComponent);
+ }
+ mEphemeralResolverComponent = null;
+ mEphemeralInstallerComponent = null;
+ mEphemeralResolverConnection = null;
+ }
} // synchronized (mPackages)
} // synchronized (mInstallLock)
@@ -2521,6 +2527,89 @@
return verifierComponentName;
}
+ private ComponentName getEphemeralResolverLPr() {
+ final String[] packageArray =
+ mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
+ if (packageArray.length == 0) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
+ }
+ return null;
+ }
+
+ Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+ final List<ResolveInfo> resolvers = queryIntentServices(resolverIntent,
+ null /*resolvedType*/, 0 /*flags*/, UserHandle.USER_SYSTEM);
+
+ final int N = resolvers.size();
+ if (N == 0) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
+ }
+ return null;
+ }
+
+ final Set<String> possiblePackages = new ArraySet<>(Arrays.asList(packageArray));
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = resolvers.get(i);
+
+ if (info.serviceInfo == null) {
+ continue;
+ }
+
+ final String packageName = info.serviceInfo.packageName;
+ if (!possiblePackages.contains(packageName)) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ + " pkg: " + packageName + ", info:" + info);
+ }
+ continue;
+ }
+
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Ephemeral resolver found;"
+ + " pkg: " + packageName + ", info:" + info);
+ }
+ return new ComponentName(packageName, info.serviceInfo.name);
+ }
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Ephemeral resolver NOT found");
+ }
+ return null;
+ }
+
+ private ComponentName getEphemeralInstallerLPr() {
+ Intent installerIntent = new Intent(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+ installerIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ final List<ResolveInfo> installers = queryIntentActivities(installerIntent,
+ PACKAGE_MIME_TYPE, 0 /*flags*/, 0 /*userId*/);
+
+ ComponentName ephemeralInstaller = null;
+
+ final int N = installers.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = installers.get(i);
+ final String packageName = info.activityInfo.packageName;
+
+ if (!info.activityInfo.applicationInfo.isSystemApp()) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Ephemeral installer is not system app;"
+ + " pkg: " + packageName + ", info:" + info);
+ }
+ continue;
+ }
+
+ if (ephemeralInstaller != null) {
+ throw new RuntimeException("There must only be one ephemeral installer");
+ }
+
+ ephemeralInstaller = new ComponentName(packageName, info.activityInfo.name);
+ }
+
+ return ephemeralInstaller;
+ }
+
private void primeDomainVerificationsLPw(int userId) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Priming domain verifications in user " + userId);
@@ -3058,23 +3147,26 @@
* purposefully done before acquiring {@link #mPackages} lock.
*/
private int augmentFlagsForUser(int flags, int userId) {
- // TODO: bring back once locking fixed
-// final IActivityManager am = ActivityManagerNative.getDefault();
-// if (am == null) {
-// // We must be early in boot, so the best we can do is assume the
-// // user is fully running.
-// return flags;
-// }
-// final long token = Binder.clearCallingIdentity();
-// try {
-// if (am.isUserRunning(userId, ActivityManager.FLAG_WITH_AMNESIA)) {
-// flags |= PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA;
-// }
-// } catch (RemoteException e) {
-// throw e.rethrowAsRuntimeException();
-// } finally {
-// Binder.restoreCallingIdentity(token);
-// }
+ if (StorageManager.isFileBasedEncryptionEnabled()) {
+ final IMountService mount = IMountService.Stub
+ .asInterface(ServiceManager.getService("mount"));
+ if (mount == null) {
+ // We must be early in boot, so the best we can do is assume the
+ // user is fully running.
+ Slog.w(TAG, "Early during boot, assuming not encrypted");
+ return flags;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mount.isUserKeyUnlocked(userId)) {
+ flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
return flags;
}
@@ -3604,6 +3696,11 @@
return;
}
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+ return;
+ }
+
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
@@ -4300,8 +4397,97 @@
false, false, false, userId);
}
+ private boolean isEphemeralAvailable(Intent intent, String resolvedType, int userId) {
+ MessageDigest digest = null;
+ try {
+ digest = MessageDigest.getInstance(EphemeralResolveInfo.SHA_ALGORITHM);
+ } catch (NoSuchAlgorithmException e) {
+ // If we can't create a digest, ignore ephemeral apps.
+ return false;
+ }
+
+ final byte[] hostBytes = intent.getData().getHost().getBytes();
+ final byte[] digestBytes = digest.digest(hostBytes);
+ int shaPrefix =
+ digestBytes[0] << 24
+ | digestBytes[1] << 16
+ | digestBytes[2] << 8
+ | digestBytes[3] << 0;
+ final List<EphemeralResolveInfo> ephemeralResolveInfoList =
+ mEphemeralResolverConnection.getEphemeralResolveInfoList(shaPrefix);
+ if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
+ // No hash prefix match; there are no ephemeral apps for this domain.
+ return false;
+ }
+ for (int i = ephemeralResolveInfoList.size() - 1; i >= 0; --i) {
+ EphemeralResolveInfo ephemeralApplication = ephemeralResolveInfoList.get(i);
+ if (!Arrays.equals(digestBytes, ephemeralApplication.getDigestBytes())) {
+ continue;
+ }
+ final List<IntentFilter> filters = ephemeralApplication.getFilters();
+ // No filters; this should never happen.
+ if (filters.isEmpty()) {
+ continue;
+ }
+ // We have a domain match; resolve the filters to see if anything matches.
+ final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
+ for (int j = filters.size() - 1; j >= 0; --j) {
+ ephemeralResolver.addFilter(filters.get(j));
+ }
+ List<ResolveInfo> ephemeralResolveList = ephemeralResolver.queryIntent(
+ intent, resolvedType, false /*defaultOnly*/, userId);
+ return !ephemeralResolveList.isEmpty();
+ }
+ // Hash or filter mis-match; no ephemeral apps for this domain.
+ return false;
+ }
+
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int userId) {
+ final boolean isWebUri = hasWebURI(intent);
+ // Check whether or not an ephemeral app exists to handle the URI.
+ if (isWebUri && mEphemeralResolverConnection != null) {
+ // Deny ephemeral apps if the user choose _ALWAYS or _ALWAYS_ASK for intent resolution.
+ boolean hasAlwaysHandler = false;
+ synchronized (mPackages) {
+ final int count = query.size();
+ for (int n=0; n<count; n++) {
+ ResolveInfo info = query.get(n);
+ String packageName = info.activityInfo.packageName;
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ // Try to get the status from User settings first
+ long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ int status = (int) (packedStatus >> 32);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+ || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ hasAlwaysHandler = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Only consider installing an ephemeral app if there isn't already a verified handler.
+ // We've determined that there's an ephemeral app available for the URI, ignore any
+ // ResolveInfo's and just return the ephemeral installer
+ if (!hasAlwaysHandler && isEphemeralAvailable(intent, resolvedType, userId)) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Resolving to the ephemeral installer");
+ }
+ // ditch the result and return a ResolveInfo to launch the ephemeral installer
+ ResolveInfo ri = new ResolveInfo(mEphemeralInstallerInfo);
+ ri.activityInfo = new ActivityInfo(ri.activityInfo);
+ // make a deep copy of the applicationInfo
+ ri.activityInfo.applicationInfo = new ApplicationInfo(
+ ri.activityInfo.applicationInfo);
+ if (userId != 0) {
+ ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
+ UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
+ }
+ return ri;
+ }
+ }
if (query != null) {
final int N = query.size();
if (N == 1) {
@@ -6148,8 +6334,8 @@
}
@Override
- public void performBootDexOpt() {
- enforceSystemOrRoot("Only the system can request dexopt be performed");
+ public void performFstrimIfNeeded() {
+ enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
@@ -6190,98 +6376,6 @@
} catch (RemoteException e) {
// Can't happen; MountService is local
}
-
- final ArraySet<PackageParser.Package> pkgs;
- synchronized (mPackages) {
- pkgs = mPackageDexOptimizer.clearDeferredDexOptPackages();
- }
-
- if (pkgs != null) {
- // Sort apps by importance for dexopt ordering. Important apps are given more priority
- // in case the device runs out of space.
- ArrayList<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>();
- // Give priority to core apps.
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkg.coreApp) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding core app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Give priority to system apps that listen for pre boot complete.
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
- PackageParser.Package pkg = it.next();
- if (pkgNames.contains(pkg.packageName)) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding pre boot system app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- it.remove();
- }
- }
- // Filter out packages that aren't recently used.
- filterRecentlyUsedApps(pkgs);
- // Add all remaining apps.
- for (PackageParser.Package pkg : pkgs) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Adding app " + sortedPkgs.size() + ": " + pkg.packageName);
- }
- sortedPkgs.add(pkg);
- }
-
- // If we want to be lazy, filter everything that wasn't recently used.
- if (mLazyDexOpt) {
- filterRecentlyUsedApps(sortedPkgs);
- }
-
- int i = 0;
- int total = sortedPkgs.size();
- File dataDir = Environment.getDataDirectory();
- long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
- if (lowThreshold == 0) {
- throw new IllegalStateException("Invalid low memory threshold");
- }
- for (PackageParser.Package pkg : sortedPkgs) {
- long usableSpace = dataDir.getUsableSpace();
- if (usableSpace < lowThreshold) {
- Log.w(TAG, "Not running dexopt on remaining apps due to low memory: " + usableSpace);
- break;
- }
- performBootDexOpt(pkg, ++i, total);
- }
- }
- }
-
- private void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs) {
- // Filter out packages that aren't recently used.
- //
- // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which
- // should do a full dexopt.
- if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) {
- int total = pkgs.size();
- int skipped = 0;
- long now = System.currentTimeMillis();
- for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
- PackageParser.Package pkg = i.next();
- long then = pkg.mLastPackageUsageTimeInMills;
- if (then + mDexOptLRUThresholdInMills < now) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
- }
- i.remove();
- skipped++;
- }
- }
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
- }
- }
}
private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
@@ -6300,54 +6394,36 @@
return pkgNames;
}
- private void performBootDexOpt(PackageParser.Package pkg, int curr, int total) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);
- }
- if (!isFirstBoot()) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(R.string.android_upgrading_apk,
- curr, total), true);
- } catch (RemoteException e) {
+ @Override
+ public void notifyPackageUse(String packageName) {
+ synchronized (mPackages) {
+ PackageParser.Package p = mPackages.get(packageName);
+ if (p == null) {
+ return;
}
- }
- PackageParser.Package p = pkg;
- synchronized (mInstallLock) {
- mPackageDexOptimizer.performDexOpt(p, null /* instruction sets */,
- false /* force dex */, false /* defer */, true /* include dependencies */,
- false /* boot complete */, false /*useJit*/);
+ p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
}
}
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOptTraced(packageName, instructionSet, false);
+ return performDexOptTraced(packageName, instructionSet);
}
- public boolean performDexOpt(
- String packageName, String instructionSet, boolean backgroundDexopt) {
- return performDexOptTraced(packageName, instructionSet, backgroundDexopt);
+ public boolean performDexOpt(String packageName, String instructionSet) {
+ return performDexOptTraced(packageName, instructionSet);
}
- private boolean performDexOptTraced(
- String packageName, String instructionSet, boolean backgroundDexopt) {
+ private boolean performDexOptTraced(String packageName, String instructionSet) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, instructionSet, backgroundDexopt);
+ return performDexOptInternal(packageName, instructionSet);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private boolean performDexOptInternal(
- String packageName, String instructionSet, boolean backgroundDexopt) {
- boolean dexopt = mLazyDexOpt || backgroundDexopt;
- boolean updateUsage = !backgroundDexopt; // Don't update usage if this is just a backgroundDexopt
- if (!dexopt && !updateUsage) {
- // We aren't going to dexopt or update usage, so bail early.
- return false;
- }
+ private boolean performDexOptInternal(String packageName, String instructionSet) {
PackageParser.Package p;
final String targetInstructionSet;
synchronized (mPackages) {
@@ -6355,14 +6431,7 @@
if (p == null) {
return false;
}
- if (updateUsage) {
- p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
- }
mPackageUsage.write(false);
- if (!dexopt) {
- // We aren't going to dexopt, so bail early.
- return false;
- }
targetInstructionSet = instructionSet != null ? instructionSet :
getPrimaryInstructionSet(p.applicationInfo);
@@ -6375,8 +6444,7 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = mPackageDexOptimizer.performDexOpt(p, instructionSets,
- false /* forceDex */, false /* defer */, true /* inclDependencies */,
- true /* boot complete */, false /*useJit*/);
+ true /* inclDependencies */);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -6426,8 +6494,7 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
final int res = mPackageDexOptimizer.performDexOpt(pkg, instructionSets,
- true /*forceDex*/, false /* defer */, true /* inclDependencies */,
- true /* boot complete */, false /*useJit*/);
+ true /* inclDependencies */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -6451,22 +6518,25 @@
return true;
}
- private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
- int[] users = sUserManager.getUserIds();
+ private void createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo)
+ throws PackageManagerException {
int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
- if (res < 0) {
- return res;
+ if (res != 0) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to install " + packageName + ": " + res);
}
+
+ final int[] users = sUserManager.getUserIds();
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(volumeUuid, packageName,
UserHandle.getUid(user, uid), user, seinfo);
- if (res < 0) {
- return res;
+ if (res != 0) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to createUserData " + packageName + ": " + res);
}
}
}
- return res;
}
private int removeDataDirsLI(String volumeUuid, String packageName) {
@@ -7036,18 +7106,6 @@
+ pkg.applicationInfo.uid + "; old data erased";
reportSettingsProblem(Log.WARN, msg);
recovered = true;
-
- // And now re-install the app.
- ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.seinfo);
- if (ret == -1) {
- // Ack should not happen!
- msg = prefix + pkg.packageName
- + " could not have data directory re-created after delete.";
- reportSettingsProblem(Log.WARN, msg);
- throw new PackageManagerException(
- INSTALL_FAILED_INSUFFICIENT_STORAGE, msg);
- }
}
if (!recovered) {
mHasSystemUidErrors = true;
@@ -7080,6 +7138,10 @@
}
}
+ // Ensure that directories are prepared
+ createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
+ pkg.applicationInfo.seinfo);
+
if (mShouldRestoreconData) {
Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
@@ -7090,14 +7152,8 @@
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.v(TAG, "Want this data dir: " + dataPath);
}
- //invoke installer to do the actual installation
- int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
+ createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
- if (ret < 0) {
- // Error from installer
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Unable to create data dirs [errorCode=" + ret + "]");
- }
}
// Get all of our default paths setup
@@ -7217,7 +7273,7 @@
" secondary=" + pkg.applicationInfo.secondaryCpuAbi);
}
- if ((scanFlags&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
//
@@ -7225,21 +7281,9 @@
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
- pkg, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, true /* boot complete */);
+ pkg, true /* boot complete */);
}
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
- forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
- (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
- }
- }
if (mFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
@@ -7291,7 +7335,7 @@
+ name + " that is not declared on system image; skipping");
}
}
- if ((scanFlags&SCAN_BOOTING) == 0) {
+ if ((scanFlags & SCAN_BOOTING) == 0) {
// If we are not booting, we need to update any applications
// that are clients of our shared library. If we are booting,
// this will all be done once the scan is complete.
@@ -7301,30 +7345,6 @@
}
}
- // We also need to dexopt any apps that are dependent on this library. Note that
- // if these fail, we should abort the install since installing the library will
- // result in some apps being broken.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- try {
- if (clientLibPkgs != null) {
- if ((scanFlags & SCAN_NO_DEX) == 0) {
- for (int i = 0; i < clientLibPkgs.size(); i++) {
- PackageParser.Package clientPkg = clientLibPkgs.get(i);
- int result = mPackageDexOptimizer.performDexOpt(clientPkg,
- null /* instruction sets */, forceDex,
- (scanFlags & SCAN_DEFER_DEX) != 0, false,
- (scanFlags & SCAN_BOOTING) == 0, false /*useJit*/);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
- "scanPackageLI failed to dexopt clientLibPkgs");
- }
- }
- }
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
@@ -7871,8 +7891,7 @@
* adds unnecessary complexity.
*/
private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
- PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt,
- boolean bootComplete) {
+ PackageParser.Package scannedPackage, boolean bootComplete) {
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -7934,22 +7953,8 @@
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer.performDexOpt(ps.pkg,
- null /* instruction sets */, forceDexOpt, deferDexOpt, true,
- bootComplete, false /*useJit*/);
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- ps.primaryCpuAbiString = null;
- ps.pkg.applicationInfo.primaryCpuAbi = null;
- return;
- } else {
- mInstaller.rmdex(ps.codePathString,
- getDexCodeInstructionSet(getPreferredInstructionSet()));
- }
+ mInstaller.rmdex(ps.codePathString,
+ getDexCodeInstructionSet(getPreferredInstructionSet()));
}
}
}
@@ -7980,6 +7985,30 @@
}
}
+ private void setUpEphemeralInstallerActivityLP(ComponentName installerComponent) {
+ final PackageParser.Package pkg = mPackages.get(installerComponent.getPackageName());
+
+ // Set up information for ephemeral installer activity
+ mEphemeralInstallerActivity.applicationInfo = pkg.applicationInfo;
+ mEphemeralInstallerActivity.name = mEphemeralInstallerComponent.getClassName();
+ mEphemeralInstallerActivity.packageName = pkg.applicationInfo.packageName;
+ mEphemeralInstallerActivity.processName = pkg.applicationInfo.packageName;
+ mEphemeralInstallerActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ mEphemeralInstallerActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
+ ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+ mEphemeralInstallerActivity.theme = 0;
+ mEphemeralInstallerActivity.exported = true;
+ mEphemeralInstallerActivity.enabled = true;
+ mEphemeralInstallerInfo.activityInfo = mEphemeralInstallerActivity;
+ mEphemeralInstallerInfo.priority = 0;
+ mEphemeralInstallerInfo.preferredOrder = 0;
+ mEphemeralInstallerInfo.match = 0;
+
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Set ephemeral installer activity: " + mEphemeralInstallerComponent);
+ }
+ }
+
private static String calculateBundledApkRoot(final String codePathString) {
final File codePath = new File(codePathString);
final File codeRoot;
@@ -9536,7 +9565,28 @@
private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
= new ArrayMap<ComponentName, PackageParser.Provider>();
private int mFlags;
- };
+ }
+
+ private static final class EphemeralIntentResolver
+ extends IntentResolver<IntentFilter, ResolveInfo> {
+ @Override
+ protected IntentFilter[] newArray(int size) {
+ return new IntentFilter[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, IntentFilter info) {
+ return true;
+ }
+
+ @Override
+ protected ResolveInfo newResult(IntentFilter info, int match, int userId) {
+ if (!sUserManager.exists(userId)) return null;
+ final ResolveInfo res = new ResolveInfo();
+ res.filter = info;
+ return res;
+ }
+ }
private static final Comparator<ResolveInfo> mResolvePrioritySorter =
new Comparator<ResolveInfo>() {
@@ -9577,8 +9627,8 @@
}
};
- final void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver,
+ final void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds) {
mHandler.post(new Runnable() {
@Override
@@ -9608,7 +9658,7 @@
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
if (DEBUG_BROADCASTS) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
@@ -9800,7 +9850,7 @@
extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, new int[] {userId});
+ packageName, extras, 0, null, null, new int[] {userId});
try {
IActivityManager am = ActivityManagerNative.getDefault();
final boolean isSystem =
@@ -12719,19 +12769,6 @@
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
-
- // Run dexopt before old package gets removed, to minimize time when app is unavailable
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
- false /* defer */, false /* inclDependencies */,
- true /*bootComplete*/, quickInstall /*useJit*/);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
- return;
- }
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -12981,7 +13018,7 @@
final boolean deleteAllUsers = (flags & PackageManager.DELETE_ALL_USERS) != 0;
final int[] users = deleteAllUsers ? sUserManager.getUserIds() : new int[]{ userId };
if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"deletePackage for user " + userId);
}
@@ -13028,8 +13065,15 @@
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
try {
if (dpm != null) {
+ final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
+ /* callingUserOnly =*/ false);
+ final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
+ : deviceOwnerComponentName.getPackageName();
// Does the package contains the device owner?
- if (dpm.isDeviceOwnerPackage(packageName)) {
+ // TODO Do we have to do it even if userId != UserHandle.USER_ALL? Otherwise,
+ // this check is probably not needed, since DO should be registered as a device
+ // admin on some user too. (Original bug for this: b/17657954)
+ if (packageName.equals(deviceOwnerPackageName)) {
return true;
}
// Does it contain a device admin for any user?
@@ -13050,6 +13094,10 @@
return false;
}
+ private boolean shouldKeepUninstalledPackageLPr(String packageName) {
+ return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
+ }
+
/**
* This method is an internal method that could be get invoked either
* to delete an installed package or to clean up a failed installation.
@@ -13117,11 +13165,11 @@
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null, null);
+ extras, 0, null, null, null);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null, null);
+ extras, 0, null, null, null);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null, null);
+ null, 0, packageName, null, null);
}
}
// Force a gc here.
@@ -13156,14 +13204,14 @@
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, removedUsers);
+ extras, 0, null, null, removedUsers);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, removedUsers);
+ extras, 0, null, null, removedUsers);
}
}
if (removedAppId >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
+ sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, 0, null, null,
removedUsers);
}
}
@@ -13472,7 +13520,9 @@
false, // blockUninstall
ps.readUserState(userId).domainVerificationStatus, 0);
if (!isSystemApp(ps)) {
- if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+ // Do not uninstall the APK if an app should be cached
+ boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
+ if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
// Other user still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
@@ -14863,7 +14913,12 @@
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ // If this is not reporting a change of the overall package, then only send it
+ // to registered receivers. We don't want to launch a swath of apps for every
+ // little component state change.
+ final int flags = !componentNames.contains(packageName)
+ ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
new int[] {UserHandle.getUserId(packageUid)});
}
@@ -15051,20 +15106,23 @@
static class DumpState {
public static final int DUMP_LIBS = 1 << 0;
public static final int DUMP_FEATURES = 1 << 1;
- public static final int DUMP_RESOLVERS = 1 << 2;
- public static final int DUMP_PERMISSIONS = 1 << 3;
- public static final int DUMP_PACKAGES = 1 << 4;
- public static final int DUMP_SHARED_USERS = 1 << 5;
- public static final int DUMP_MESSAGES = 1 << 6;
- public static final int DUMP_PROVIDERS = 1 << 7;
- public static final int DUMP_VERIFIERS = 1 << 8;
- public static final int DUMP_PREFERRED = 1 << 9;
- public static final int DUMP_PREFERRED_XML = 1 << 10;
- public static final int DUMP_KEYSETS = 1 << 11;
- public static final int DUMP_VERSION = 1 << 12;
- public static final int DUMP_INSTALLS = 1 << 13;
- public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
- public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
+ public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2;
+ public static final int DUMP_SERVICE_RESOLVERS = 1 << 3;
+ public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4;
+ public static final int DUMP_CONTENT_RESOLVERS = 1 << 5;
+ public static final int DUMP_PERMISSIONS = 1 << 6;
+ public static final int DUMP_PACKAGES = 1 << 7;
+ public static final int DUMP_SHARED_USERS = 1 << 8;
+ public static final int DUMP_MESSAGES = 1 << 9;
+ public static final int DUMP_PROVIDERS = 1 << 10;
+ public static final int DUMP_VERIFIERS = 1 << 11;
+ public static final int DUMP_PREFERRED = 1 << 12;
+ public static final int DUMP_PREFERRED_XML = 1 << 13;
+ public static final int DUMP_KEYSETS = 1 << 14;
+ public static final int DUMP_VERSION = 1 << 15;
+ public static final int DUMP_INSTALLS = 1 << 16;
+ public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17;
+ public static final int DUMP_DOMAIN_PREFERRED = 1 << 18;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -15163,9 +15221,9 @@
pw.println(" -h: print this help");
pw.println(" cmd may be one of:");
pw.println(" l[ibraries]: list known shared libraries");
- pw.println(" f[ibraries]: list device features");
+ pw.println(" f[eatures]: list device features");
pw.println(" k[eysets]: print known keysets");
- pw.println(" r[esolvers]: dump intent resolvers");
+ pw.println(" r[esolvers] [activity|service|receiver|content]: dump intent resolvers");
pw.println(" perm[issions]: dump permissions");
pw.println(" permission [name ...]: dump declaration and use of given permission");
pw.println(" pref[erred]: print preferred package settings");
@@ -15232,7 +15290,29 @@
} else if ("f".equals(cmd) || "features".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_FEATURES);
} else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
- dumpState.setDump(DumpState.DUMP_RESOLVERS);
+ if (opti >= args.length) {
+ dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS
+ | DumpState.DUMP_SERVICE_RESOLVERS
+ | DumpState.DUMP_RECEIVER_RESOLVERS
+ | DumpState.DUMP_CONTENT_RESOLVERS);
+ } else {
+ while (opti < args.length) {
+ String name = args[opti];
+ if ("a".equals(name) || "activity".equals(name)) {
+ dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS);
+ } else if ("s".equals(name) || "service".equals(name)) {
+ dumpState.setDump(DumpState.DUMP_SERVICE_RESOLVERS);
+ } else if ("r".equals(name) || "receiver".equals(name)) {
+ dumpState.setDump(DumpState.DUMP_RECEIVER_RESOLVERS);
+ } else if ("c".equals(name) || "content".equals(name)) {
+ dumpState.setDump(DumpState.DUMP_CONTENT_RESOLVERS);
+ } else {
+ pw.println("Error: unknown resolver table type: " + name);
+ return;
+ }
+ opti++;
+ }
+ }
} else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PERMISSIONS);
} else if ("permission".equals(cmd)) {
@@ -15399,22 +15479,28 @@
}
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
: "Activity Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
: "Receiver Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
: "Service Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
: "Provider Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
@@ -15861,7 +15947,7 @@
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
+ sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null);
}
}
@@ -16178,13 +16264,14 @@
}
}
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
final UserManager um = mContext.getSystemService(UserManager.class);
for (UserInfo user : um.getUsers()) {
final File userDir = Environment.getDataUserDirectory(volumeUuid, user.id);
if (userDir.exists()) continue;
try {
- UserManagerService.prepareUserDirectory(mContext, volumeUuid, user.id);
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber);
UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
} catch (IOException e) {
Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -16565,23 +16652,26 @@
}
/** Called by UserManagerService */
- void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
- mDirtyUsers.remove(userHandle);
- mSettings.removeUserLPw(userHandle);
- mPendingBroadcasts.remove(userHandle);
- if (mInstaller != null) {
- // Technically, we shouldn't be doing this with the package lock
- // held. However, this is very rare, and there is already so much
- // other disk I/O going on, that we'll let it slide for now.
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
- mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+ void cleanUpUser(UserManagerService userManager, int userHandle) {
+ synchronized (mPackages) {
+ mDirtyUsers.remove(userHandle);
+ mUserNeedsBadging.delete(userHandle);
+ mSettings.removeUserLPw(userHandle);
+ mPendingBroadcasts.remove(userHandle);
+ }
+ synchronized (mInstallLock) {
+ if (mInstaller != null) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
+ mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+ }
+ }
+ synchronized (mPackages) {
+ removeUnusedPackagesLILPw(userManager, userHandle);
}
}
- mUserNeedsBadging.delete(userHandle);
- removeUnusedPackagesLILPw(userManager, userHandle);
}
/**
@@ -16606,15 +16696,21 @@
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, "Checking package " + packageName);
}
- boolean keep = false;
- for (int i = 0; i < users.length; i++) {
- if (users[i] != userHandle && ps.getInstalled(users[i])) {
- keep = true;
- if (DEBUG_CLEAN_APKS) {
- Slog.i(TAG, " Keeping package " + packageName + " for user "
- + users[i]);
+ boolean keep = shouldKeepUninstalledPackageLPr(packageName);
+ if (keep) {
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Keeping package " + packageName + " - requested by DO");
+ }
+ } else {
+ for (int i = 0; i < users.length; i++) {
+ if (users[i] != userHandle && ps.getInstalled(users[i])) {
+ keep = true;
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Keeping package " + packageName + " for user "
+ + users[i]);
+ }
+ break;
}
- break;
}
}
if (!keep) {
@@ -16631,12 +16727,18 @@
}
/** Called by UserManagerService */
- void createNewUserLILPw(int userHandle) {
+ void createNewUser(int userHandle) {
if (mInstaller != null) {
- mInstaller.createUserConfig(userHandle);
- mSettings.createNewUserLILPw(this, mInstaller, userHandle);
- applyFactoryDefaultBrowserLPw(userHandle);
- primeDomainVerificationsLPw(userHandle);
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ mInstaller.createUserConfig(userHandle);
+ mSettings.createNewUserLILPw(this, mInstaller, userHandle);
+ }
+ }
+ synchronized (mPackages) {
+ applyFactoryDefaultBrowserLPw(userHandle);
+ primeDomainVerificationsLPw(userHandle);
+ }
}
}
@@ -16810,24 +16912,20 @@
}
}
- public void getUsageStatsIfNoPackageUsageInfo() {
- if (!mPackageUsage.isHistoricalPackageUsageAvailable()) {
- UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
- if (usm == null) {
- throw new IllegalStateException("UsageStatsManager must be initialized");
- }
- long now = System.currentTimeMillis();
- Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now);
- for (Map.Entry<String, UsageStats> entry : stats.entrySet()) {
- String packageName = entry.getKey();
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- continue;
+ private void deletePackageIfUnusedLPr(final String packageName) {
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ return;
+ }
+ if (!ps.isAnyInstalled(sUserManager.getUserIds())) {
+ // TODO Implement atomic delete if package is unused
+ // It is currently possible that the package will be deleted even if it is installed
+ // after this method returns.
+ mHandler.post(new Runnable() {
+ public void run() {
+ deletePackageX(packageName, 0, PackageManager.DELETE_ALL_USERS);
}
- UsageStats usage = entry.getValue();
- pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed();
- mPackageUsage.mIsHistoricalPackageUsageAvailable = true;
- }
+ });
}
}
@@ -17068,6 +17166,34 @@
packageName, userId);
}
}
+
+ @Override
+ public void setKeepUninstalledPackages(final List<String> packageList) {
+ Preconditions.checkNotNull(packageList);
+ List<String> removedFromList = null;
+ synchronized (mPackages) {
+ if (mKeepUninstalledPackages != null) {
+ final int packagesCount = mKeepUninstalledPackages.size();
+ for (int i = 0; i < packagesCount; i++) {
+ String oldPackage = mKeepUninstalledPackages.get(i);
+ if (packageList != null && packageList.contains(oldPackage)) {
+ continue;
+ }
+ if (removedFromList == null) {
+ removedFromList = new ArrayList<>();
+ }
+ removedFromList.add(oldPackage);
+ }
+ }
+ mKeepUninstalledPackages = new ArrayList<>(packageList);
+ if (removedFromList != null) {
+ final int removedCount = removedFromList.size();
+ for (int i = 0; i < removedCount; i++) {
+ deletePackageIfUnusedLPr(removedFromList.get(i));
+ }
+ }
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d7176fd..dbb5818 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -35,6 +35,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
@@ -46,6 +47,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.PrintWriterPrinter;
import com.android.internal.util.SizedInputStream;
import libcore.io.IoUtils;
@@ -56,6 +58,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -68,6 +71,7 @@
final IPackageManager mInterface;
final private WeakHashMap<String, Resources> mResourceCache =
new WeakHashMap<String, Resources>();
+ int mTargetUser;
PackageManagerShellCommand(PackageManagerService service) {
mInterface = service;
@@ -97,6 +101,12 @@
return runList();
case "uninstall":
return runUninstall();
+ case "query-intent-activities":
+ return runQueryIntentActivities();
+ case "query-intent-services":
+ return runQueryIntentServices();
+ case "query-intent-receivers":
+ return runQueryIntentReceivers();
default:
return handleDefaultCommands(cmd);
}
@@ -340,7 +350,7 @@
listThirdParty = true;
break;
case "--user":
- userId = Integer.parseInt(getNextArg());
+ userId = UserHandle.parseUserArg(getNextArgRequired());
break;
default:
pw.println("Error: Unknown option: " + opt);
@@ -487,7 +497,7 @@
flags |= PackageManager.DELETE_KEEP_DATA;
break;
case "--user":
- userId = Integer.parseInt(getNextArg());
+ userId = UserHandle.parseUserArg(getNextArgRequired());
break;
default:
pw.println("Error: Unknown option: " + opt);
@@ -538,6 +548,104 @@
}
}
+ private Intent parseIntentAndUser() throws URISyntaxException {
+ mTargetUser = UserHandle.USER_CURRENT;
+ Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
+ @Override
+ public boolean handleOption(String opt, ShellCommand cmd) {
+ if ("--user".equals(opt)) {
+ mTargetUser = UserHandle.parseUserArg(cmd.getNextArgRequired());
+ return true;
+ }
+ return false;
+ }
+ });
+ mTargetUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), mTargetUser, false, false, null, null);
+ return intent;
+ }
+
+ private int runQueryIntentActivities() {
+ Intent intent;
+ try {
+ intent = parseIntentAndUser();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ try {
+ List<ResolveInfo> result = mInterface.queryIntentActivities(intent, null, 0,
+ mTargetUser);
+ PrintWriter pw = getOutPrintWriter();
+ if (result == null || result.size() <= 0) {
+ pw.println("No activities found");
+ } else {
+ pw.print(result.size()); pw.println(" activities found:");
+ PrintWriterPrinter pr = new PrintWriterPrinter(pw);
+ for (int i=0; i<result.size(); i++) {
+ pw.print(" Activity #"); pw.print(i); pw.println(":");
+ result.get(i).dump(pr, " ");
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed calling service", e);
+ }
+ return 0;
+ }
+
+ private int runQueryIntentServices() {
+ Intent intent;
+ try {
+ intent = parseIntentAndUser();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ try {
+ List<ResolveInfo> result = mInterface.queryIntentServices(intent, null, 0,
+ mTargetUser);
+ PrintWriter pw = getOutPrintWriter();
+ if (result == null || result.size() <= 0) {
+ pw.println("No services found");
+ } else {
+ pw.print(result.size()); pw.println(" services found:");
+ PrintWriterPrinter pr = new PrintWriterPrinter(pw);
+ for (int i=0; i<result.size(); i++) {
+ pw.print(" Service #"); pw.print(i); pw.println(":");
+ result.get(i).dump(pr, " ");
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed calling service", e);
+ }
+ return 0;
+ }
+
+ private int runQueryIntentReceivers() {
+ Intent intent;
+ try {
+ intent = parseIntentAndUser();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ try {
+ List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, null, 0,
+ mTargetUser);
+ PrintWriter pw = getOutPrintWriter();
+ if (result == null || result.size() <= 0) {
+ pw.println("No receivers found");
+ } else {
+ pw.print(result.size()); pw.println(" receivers found:");
+ PrintWriterPrinter pr = new PrintWriterPrinter(pw);
+ for (int i=0; i<result.size(); i++) {
+ pw.print(" Receiver #"); pw.print(i); pw.println(":");
+ result.get(i).dump(pr, " ");
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed calling service", e);
+ }
+ return 0;
+ }
+
private static class InstallParams {
SessionParams sessionParams;
String installerPackageName;
@@ -598,7 +706,7 @@
sessionParams.abiOverride = checkAbiArgument(getNextArg());
break;
case "--user":
- params.userId = Integer.parseInt(getNextArg());
+ params.userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case "--install-location":
sessionParams.installLocation = Integer.parseInt(getNextArg());
@@ -892,7 +1000,7 @@
pw.println(" the text in FILTER.");
pw.println(" Options:");
pw.println(" -f: see their associated file");
- pw.println(" -d: filter to only show disbled packages");
+ pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
pw.println(" -3: filter to only show third party packages");
@@ -908,7 +1016,14 @@
pw.println(" -s: short summary");
pw.println(" -d: only list dangerous permissions");
pw.println(" -u: list only the permissions users will see");
- pw.println("");
+ pw.println(" query-intent-activities [--user USER_ID] INTENT");
+ pw.println(" Prints all activities that can handle the given Intent.");
+ pw.println(" query-intent-services [--user USER_ID] INTENT");
+ pw.println(" Prints all services that can handle the given Intent.");
+ pw.println(" query-intent-receivers [--user USER_ID] INTENT");
+ pw.println(" Prints all broadcast receivers that can handle the given Intent.");
+ pw.println();
+ Intent.printIntentArgsHelp(pw , "");
}
private static class LocalIntentReceiver {
@@ -940,4 +1055,3 @@
}
}
}
-
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 5d8b1d2..903d12b 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -103,6 +103,9 @@
// Append privapp to existing seinfo label
private static final String PRIVILEGED_APP_STR = ":privapp";
+ // Append autoplay to existing seinfo label
+ private static final String AUTOPLAY_APP_STR = ":autoplayapp";
+
/**
* Load the mac_permissions.xml file containing all seinfo assignments used to
* label apps. The loaded mac_permissions.xml file is determined by the
@@ -316,6 +319,9 @@
}
}
+ if (pkg.applicationInfo.isAutoPlayApp())
+ pkg.applicationInfo.seinfo += AUTOPLAY_APP_STR;
+
if (pkg.applicationInfo.isPrivilegedApp())
pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index de14739..99aa30b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3802,8 +3802,7 @@
if ((flags & PackageManager.GET_ENCRYPTION_UNAWARE_COMPONENTS) != 0) {
return true;
}
- if ((flags & PackageManager.FLAG_USER_RUNNING_WITH_AMNESIA) != 0) {
- // When running with amnesia, we can only run encryption-aware apps
+ if ((flags & PackageManager.MATCH_ENCRYPTION_AWARE_ONLY) != 0) {
return componentInfo.encryptionAware;
}
return true;
@@ -3861,7 +3860,7 @@
if (pkgSetting.getNotLaunched(userId)) {
if (pkgSetting.installerPackageName != null) {
yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
- pkgSetting.name, null,
+ pkgSetting.name, null, 0,
pkgSetting.installerPackageName, null, new int[] {userId});
}
pkgSetting.setNotLaunched(false, userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9a87abe..ff829ff 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,6 @@
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.admin.DevicePolicyManager;
-import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -36,6 +35,7 @@
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -48,7 +48,6 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCommand;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
@@ -97,15 +96,15 @@
*
* Method naming convention:
* <ul>
- * <li> Methods suffixed with "LILP" should be called within {@link #mInstallLock} and
- * {@link #mPackagesLock} locks obtained in the respective order.
+ * <li> Methods suffixed with "LP" should be called within the {@link #mPackagesLock} lock.
* <li> Methods suffixed with "LR" should be called within the {@link #mRestrictionsLock} lock.
* <li> Methods suffixed with "LU" should be called within the {@link #mUsersLock} lock.
* </ul>
*/
public class UserManagerService extends IUserManager.Stub {
private static final String LOG_TAG = "UserManagerService";
- private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+ static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+ private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
private static final String TAG_NAME = "name";
private static final String ATTR_FLAGS = "flags";
@@ -124,6 +123,7 @@
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
private static final String TAG_RESTRICTIONS = "restrictions";
+ private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String ATTR_KEY = "key";
@@ -164,7 +164,6 @@
private final Context mContext;
private final PackageManagerService mPm;
- private final Object mInstallLock;
private final Object mPackagesLock;
// Short-term lock for internal state, when interaction/sync with PM is not required
private final Object mUsersLock = new Object();
@@ -208,13 +207,28 @@
private final SparseArray<Bundle> mCachedEffectiveUserRestrictions = new SparseArray<>();
/**
- * User restrictions that have already been applied in {@link #applyUserRestrictionsLR}. We
- * use it to detect restrictions that have changed since the last
- * {@link #applyUserRestrictionsLR} call.
+ * User restrictions that have already been applied in
+ * {@link #updateUserRestrictionsInternalLR(Bundle, int)}. We use it to detect restrictions
+ * that have changed since the last
+ * {@link #updateUserRestrictionsInternalLR(Bundle, int)} call.
*/
@GuardedBy("mRestrictionsLock")
private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>();
+ /**
+ * User restrictions set by {@link DevicePolicyManager} that should be applied to all users,
+ * including guests.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private Bundle mDevicePolicyGlobalUserRestrictions;
+
+ /**
+ * User restrictions set by {@link DevicePolicyManager} for each user.
+ */
+ @GuardedBy("mRestrictionsLock")
+ private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>();
+
+ @GuardedBy("mGuestRestrictions")
private final Bundle mGuestRestrictions = new Bundle();
/**
@@ -226,6 +240,7 @@
@GuardedBy("mUsersLock")
private int[] mUserIds;
+ @GuardedBy("mPackagesLock")
private int mNextSerialNumber;
private int mUserVersion = 0;
@@ -245,11 +260,9 @@
}
}
- /**
- * Available for testing purposes.
- */
- UserManagerService(File dataDir, File baseUserPath) {
- this(null, null, new Object(), new Object(), dataDir, baseUserPath);
+ @VisibleForTesting
+ UserManagerService(File dataDir) {
+ this(null, null, new Object(), dataDir);
}
/**
@@ -257,78 +270,61 @@
* associated with the package manager, and the given lock is the
* package manager's own lock.
*/
- UserManagerService(Context context, PackageManagerService pm,
- Object installLock, Object packagesLock) {
- this(context, pm, installLock, packagesLock,
- Environment.getDataDirectory(),
- new File(Environment.getDataDirectory(), "user"));
+ UserManagerService(Context context, PackageManagerService pm, Object packagesLock) {
+ this(context, pm, packagesLock, Environment.getDataDirectory());
}
- /**
- * Available for testing purposes.
- */
private UserManagerService(Context context, PackageManagerService pm,
- Object installLock, Object packagesLock,
- File dataDir, File baseUserPath) {
+ Object packagesLock, File dataDir) {
mContext = context;
mPm = pm;
- mInstallLock = installLock;
mPackagesLock = packagesLock;
mHandler = new MainHandler();
- synchronized (mInstallLock) {
- synchronized (mPackagesLock) {
- mUsersDir = new File(dataDir, USER_INFO_DIR);
- mUsersDir.mkdirs();
- // Make zeroth user directory, for services to migrate their files to that location
- File userZeroDir = new File(mUsersDir, String.valueOf(UserHandle.USER_SYSTEM));
- userZeroDir.mkdirs();
- FileUtils.setPermissions(mUsersDir.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG
- |FileUtils.S_IROTH|FileUtils.S_IXOTH,
- -1, -1);
- mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
- initDefaultGuestRestrictions();
- readUserListLILP();
- sInstance = this;
- }
+ synchronized (mPackagesLock) {
+ mUsersDir = new File(dataDir, USER_INFO_DIR);
+ mUsersDir.mkdirs();
+ // Make zeroth user directory, for services to migrate their files to that location
+ File userZeroDir = new File(mUsersDir, String.valueOf(UserHandle.USER_SYSTEM));
+ userZeroDir.mkdirs();
+ FileUtils.setPermissions(mUsersDir.toString(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
+ -1, -1);
+ mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ initDefaultGuestRestrictions();
+ readUserListLP();
+ sInstance = this;
}
mLocalService = new LocalService();
LocalServices.addService(UserManagerInternal.class, mLocalService);
}
void systemReady() {
- synchronized (mInstallLock) {
- synchronized (mPackagesLock) {
- synchronized (mUsersLock) {
- // Prune out any partially created/partially removed users.
- ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- UserInfo ui = mUsers.valueAt(i);
- if ((ui.partial || ui.guestToRemove) && i != 0) {
- partials.add(ui);
- }
- }
- final int partialsSize = partials.size();
- for (int i = 0; i < partialsSize; i++) {
- UserInfo ui = partials.get(i);
- Slog.w(LOG_TAG, "Removing partially created user " + ui.id
- + " (name=" + ui.name + ")");
- removeUserStateLILP(ui.id);
- }
+ // Prune out any partially created/partially removed users.
+ ArrayList<UserInfo> partials = new ArrayList<>();
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ UserInfo ui = mUsers.valueAt(i);
+ if ((ui.partial || ui.guestToRemove) && i != 0) {
+ partials.add(ui);
}
}
}
+ final int partialsSize = partials.size();
+ for (int i = 0; i < partialsSize; i++) {
+ UserInfo ui = partials.get(i);
+ Slog.w(LOG_TAG, "Removing partially created user " + ui.id
+ + " (name=" + ui.name + ")");
+ removeUserState(ui.id);
+ }
+
onUserForeground(UserHandle.USER_SYSTEM);
+
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
- for (int i = 0; i < mUserIds.length; ++i) {
- final int userId = mUserIds[i];
- try {
- mAppOpsService.setUserRestrictions(getEffectiveUserRestrictions(userId), userId);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
- }
+
+ synchronized (mRestrictionsLock) {
+ applyUserRestrictionsLR(UserHandle.USER_SYSTEM);
}
}
@@ -339,7 +335,7 @@
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
- if (ui.isPrimary()) {
+ if (ui.isPrimary() && !mRemovingUserIds.get(ui.id)) {
return ui;
}
}
@@ -409,7 +405,7 @@
@Override
public int getCredentialOwnerProfile(int userHandle) {
checkManageUsersPermission("get the credential owner");
- if (!mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ if (!StorageManager.isFileBasedEncryptionEnabled()) {
synchronized (mUsersLock) {
UserInfo profileParent = getProfileParentLU(userHandle);
if (profileParent != null) {
@@ -517,7 +513,7 @@
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
// restricted profile can be created if there is no DO set and the admin user has no PO
- return dpm.getDeviceOwner() == null && dpm.getProfileOwnerAsUser(userId) == null;
+ return !dpm.isDeviceManaged() && dpm.getProfileOwnerAsUser(userId) == null;
}
/*
@@ -627,17 +623,22 @@
public void makeInitialized(int userId) {
checkManageUsersPermission("makeInitialized");
- synchronized (mPackagesLock) {
- UserInfo info = getUserInfoNoChecks(userId);
+ boolean scheduleWriteUser = false;
+ UserInfo info;
+ synchronized (mUsersLock) {
+ info = mUsers.get(userId);
if (info == null || info.partial) {
Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
- // TODO Check if we should return here instead of a null check below
+ return;
}
- if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ if ((info.flags & UserInfo.FLAG_INITIALIZED) == 0) {
info.flags |= UserInfo.FLAG_INITIALIZED;
- scheduleWriteUser(info);
+ scheduleWriteUser = true;
}
}
+ if (scheduleWriteUser) {
+ scheduleWriteUser(info);
+ }
}
/**
@@ -645,17 +646,18 @@
* restrictions.
*/
private void initDefaultGuestRestrictions() {
- if (mGuestRestrictions.isEmpty()) {
- mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
- mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
+ synchronized (mGuestRestrictions) {
+ if (mGuestRestrictions.isEmpty()) {
+ mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
+ mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
+ }
}
}
@Override
public Bundle getDefaultGuestRestrictions() {
checkManageUsersPermission("getDefaultGuestRestrictions");
- // TODO Switch to mGuestRestrictions for locking
- synchronized (mPackagesLock) {
+ synchronized (mGuestRestrictions) {
return new Bundle(mGuestRestrictions);
}
}
@@ -663,28 +665,81 @@
@Override
public void setDefaultGuestRestrictions(Bundle restrictions) {
checkManageUsersPermission("setDefaultGuestRestrictions");
- synchronized (mInstallLock) {
- synchronized (mPackagesLock) {
- mGuestRestrictions.clear();
- mGuestRestrictions.putAll(restrictions);
- writeUserListLILP();
+ synchronized (mGuestRestrictions) {
+ mGuestRestrictions.clear();
+ mGuestRestrictions.putAll(restrictions);
+ }
+ synchronized (mPackagesLock) {
+ writeUserListLP();
+ }
+ }
+
+ /**
+ * See {@link UserManagerInternal#setDevicePolicyUserRestrictions(int, Bundle, Bundle)}
+ */
+ void setDevicePolicyUserRestrictions(int userId, @NonNull Bundle local,
+ @Nullable Bundle global) {
+ Preconditions.checkNotNull(local);
+ boolean globalChanged = false;
+ boolean localChanged;
+ synchronized (mRestrictionsLock) {
+ if (global != null) {
+ // Update global.
+ globalChanged = !UserRestrictionsUtils.areEqual(
+ mDevicePolicyGlobalUserRestrictions, global);
+ if (globalChanged) {
+ mDevicePolicyGlobalUserRestrictions = global;
+ }
+ }
+ {
+ // Update local.
+ final Bundle prev = mDevicePolicyLocalUserRestrictions.get(userId);
+ localChanged = !UserRestrictionsUtils.areEqual(prev, local);
+ if (localChanged) {
+ mDevicePolicyLocalUserRestrictions.put(userId, local);
+ }
+ }
+ }
+ if (DBG) {
+ Log.d(LOG_TAG, "setDevicePolicyUserRestrictions: userId=" + userId
+ + " global=" + global + (globalChanged ? " (changed)" : "")
+ + " local=" + local + (localChanged ? " (changed)" : "")
+ );
+ }
+ // Don't call them within the mRestrictionsLock.
+ synchronized (mPackagesLock) {
+ if (globalChanged) {
+ writeUserListLP();
+ }
+ if (localChanged) {
+ writeUserLP(getUserInfoNoChecks(userId));
+ }
+ }
+
+ synchronized (mRestrictionsLock) {
+ if (globalChanged) {
+ applyUserRestrictionsForAllUsersLR();
+ } else if (localChanged) {
+ applyUserRestrictionsLR(userId);
}
}
}
@GuardedBy("mRestrictionsLock")
private Bundle computeEffectiveUserRestrictionsLR(int userId) {
- final DevicePolicyManagerInternal dpmi =
- LocalServices.getService(DevicePolicyManagerInternal.class);
- final Bundle systemRestrictions = mBaseUserRestrictions.get(userId);
+ final Bundle baseRestrictions =
+ UserRestrictionsUtils.nonNull(mBaseUserRestrictions.get(userId));
+ final Bundle global = mDevicePolicyGlobalUserRestrictions;
+ final Bundle local = mDevicePolicyLocalUserRestrictions.get(userId);
- final Bundle effective;
- if (dpmi == null) {
- // TODO Make sure it's because DPMS is disabled and not because we called it too early.
- effective = systemRestrictions;
- } else {
- effective = dpmi.getComposedUserRestrictions(userId, systemRestrictions);
+ if (UserRestrictionsUtils.isEmpty(global) && UserRestrictionsUtils.isEmpty(local)) {
+ // Common case first.
+ return baseRestrictions;
}
+ final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+ UserRestrictionsUtils.merge(effective, global);
+ UserRestrictionsUtils.merge(effective, local);
+
return effective;
}
@@ -720,30 +775,17 @@
*/
@Override
public Bundle getUserRestrictions(int userId) {
- Bundle restrictions = getEffectiveUserRestrictions(userId);
- return restrictions != null ? new Bundle(restrictions) : new Bundle();
+ return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
}
@Override
public void setUserRestriction(String key, boolean value, int userId) {
checkManageUsersPermission("setUserRestriction");
- if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) {
- setUserRestrictionNoCheck(key, value, userId);
- }
- }
-
- @Override
- public void setSystemControlledUserRestriction(String key, boolean value, int userId) {
- checkSystemOrRoot("setSystemControlledUserRestriction");
- setUserRestrictionNoCheck(key, value, userId);
- }
-
- private void setUserRestrictionNoCheck(String key, boolean value, int userId) {
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
- final Bundle newRestrictions = new Bundle();
- UserRestrictionsUtils.merge(newRestrictions, mBaseUserRestrictions.get(userId));
+ final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ mBaseUserRestrictions.get(userId));
newRestrictions.putBoolean(key, value);
updateUserRestrictionsInternalLR(newRestrictions, userId);
@@ -751,75 +793,70 @@
}
/**
- * Optionally updating user restrictions, calculate the effective user restrictions by
- * consulting {@link com.android.server.devicepolicy.DevicePolicyManagerService} and also
- * apply it to {@link com.android.server.AppOpsService}.
- * TODO applyUserRestrictionsLocked() should also apply to system settings.
+ * Optionally updating user restrictions, calculate the effective user restrictions and also
+ * propagate to other services and system settings.
*
- * @param newRestrictions User restrictions to set. If null, only the effective restrictions
- * will be updated. Note don't pass an existing Bundle in {@link #mBaseUserRestrictions}
- * or {@link #mCachedEffectiveUserRestrictions}; that'll most likely cause a sub
+ * @param newRestrictions User restrictions to set.
+ * If null, will not update user restrictions and only does the propagation.
* @param userId target user ID.
*/
@GuardedBy("mRestrictionsLock")
private void updateUserRestrictionsInternalLR(
@Nullable Bundle newRestrictions, int userId) {
- if (DBG) {
- Log.d(LOG_TAG, "updateUserRestrictionsInternalLocked userId=" + userId
- + " bundle=" + newRestrictions);
- }
- // Update system restrictions.
+
+ final Bundle prevAppliedRestrictions = UserRestrictionsUtils.nonNull(
+ mAppliedUserRestrictions.get(userId));
+
+ // Update base restrictions.
if (newRestrictions != null) {
// If newRestrictions == the current one, it's probably a bug.
- Preconditions.checkState(mBaseUserRestrictions.get(userId) != newRestrictions);
+ final Bundle prevBaseRestrictions = mBaseUserRestrictions.get(userId);
+
+ Preconditions.checkState(prevBaseRestrictions != newRestrictions);
Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId)
!= newRestrictions);
- mBaseUserRestrictions.put(userId, newRestrictions);
- scheduleWriteUser(mUsers.get(userId));
+
+ if (!UserRestrictionsUtils.areEqual(prevBaseRestrictions, newRestrictions)) {
+ mBaseUserRestrictions.put(userId, newRestrictions);
+ scheduleWriteUser(getUserInfoNoChecks(userId));
+ }
}
final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
mCachedEffectiveUserRestrictions.put(userId, effective);
- applyUserRestrictionsLR(userId, effective);
- }
-
- @GuardedBy("mRestrictionsLock")
- private void applyUserRestrictionsLR(int userId, Bundle newRestrictions) {
- if (newRestrictions == null) {
- newRestrictions = Bundle.EMPTY;
- }
-
- Bundle prevRestrictions = mAppliedUserRestrictions.get(userId);
- if (prevRestrictions == null) {
- prevRestrictions = Bundle.EMPTY;
- }
-
+ // Apply the new restrictions.
if (DBG) {
- Log.d(LOG_TAG, "applyUserRestrictionsRL userId=" + userId
- + " new=" + newRestrictions + " prev=" + prevRestrictions);
+ debug("Applying user restrictions: userId=" + userId
+ + " new=" + effective + " prev=" + prevAppliedRestrictions);
}
- final long token = Binder.clearCallingIdentity();
- try {
- mAppOpsService.setUserRestrictions(newRestrictions, userId);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
- } finally {
- Binder.restoreCallingIdentity(token);
+ if (mAppOpsService != null) { // We skip it until system-ready.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mAppOpsService.setUserRestrictions(effective, userId);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
- UserRestrictionsUtils.applyUserRestrictionsLR(
- mContext, userId, newRestrictions, prevRestrictions);
+ propagateUserRestrictionsLR(userId, effective, prevAppliedRestrictions);
- notifyUserRestrictionsListeners(userId, newRestrictions, prevRestrictions);
-
- mAppliedUserRestrictions.put(userId, new Bundle(newRestrictions));
+ mAppliedUserRestrictions.put(userId, new Bundle(effective));
}
- private void notifyUserRestrictionsListeners(final int userId,
+ private void propagateUserRestrictionsLR(final int userId,
Bundle newRestrictions, Bundle prevRestrictions) {
+ // Note this method doesn't touch any state, meaning it doesn't require mRestrictionsLock
+ // actually, but we still need some kind of synchronization otherwise we might end up
+ // calling listeners out-of-order, thus "LR".
+
+ if (UserRestrictionsUtils.areEqual(newRestrictions, prevRestrictions)) {
+ return;
+ }
final Bundle newRestrictionsFinal = new Bundle(newRestrictions);
final Bundle prevRestrictionsFinal = new Bundle(prevRestrictions);
@@ -827,6 +864,11 @@
mHandler.post(new Runnable() {
@Override
public void run() {
+ synchronized (mRestrictionsLock) {
+ UserRestrictionsUtils.applyUserRestrictionsLR(
+ mContext, userId, newRestrictionsFinal, prevRestrictionsFinal);
+ }
+
final UserRestrictionsListener[] listeners;
synchronized (mUserRestrictionsListeners) {
listeners = new UserRestrictionsListener[mUserRestrictionsListeners.size()];
@@ -840,13 +882,17 @@
});
}
- @GuardedBy("mRestrictionsLock")
- private void updateEffectiveUserRestrictionsLR(int userId) {
+ // Package private for the inner class.
+ void applyUserRestrictionsLR(int userId) {
updateUserRestrictionsInternalLR(null, userId);
}
@GuardedBy("mRestrictionsLock")
- private void updateEffectiveUserRestrictionsForAllUsersLR() {
+ // Package private for the inner class.
+ void applyUserRestrictionsForAllUsersLR() {
+ if (DBG) {
+ debug("applyUserRestrictionsForAllUsersLR");
+ }
// First, invalidate all cached values.
mCachedEffectiveUserRestrictions.clear();
@@ -867,10 +913,9 @@
// It's okay if a new user has started after the getRunningUserIds() call,
// because we'll do the same thing (re-calculate the restrictions and apply)
// when we start a user.
- // TODO: "Apply restrictions upon user start hasn't been implemented. Implement it.
synchronized (mRestrictionsLock) {
for (int i = 0; i < runningUsers.length; i++) {
- updateUserRestrictionsInternalLR(null, runningUsers[i]);
+ applyUserRestrictionsLR(runningUsers[i]);
}
}
}
@@ -996,9 +1041,9 @@
}
}
- private void readUserListLILP() {
+ private void readUserListLP() {
if (!mUserListFile.exists()) {
- fallbackToSingleUserLILP();
+ fallbackToSingleUserLP();
return;
}
FileInputStream fis = null;
@@ -1015,7 +1060,7 @@
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUserLILP();
+ fallbackToSingleUserLP();
return;
}
@@ -1031,12 +1076,14 @@
}
}
+ final Bundle newDevicePolicyGlobalUserRestrictions = new Bundle();
+
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
final String name = parser.getName();
if (name.equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUserLILP(Integer.parseInt(id));
+ UserInfo user = readUserLP(Integer.parseInt(id));
if (user != null) {
synchronized (mUsersLock) {
@@ -1051,8 +1098,14 @@
&& type != XmlPullParser.END_TAG) {
if (type == XmlPullParser.START_TAG) {
if (parser.getName().equals(TAG_RESTRICTIONS)) {
- UserRestrictionsUtils
- .readRestrictions(parser, mGuestRestrictions);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .readRestrictions(parser, mGuestRestrictions);
+ }
+ } else if (parser.getName().equals(TAG_DEVICE_POLICY_RESTRICTIONS)
+ ) {
+ UserRestrictionsUtils.readRestrictions(parser,
+ newDevicePolicyGlobalUserRestrictions);
}
break;
}
@@ -1060,26 +1113,23 @@
}
}
}
- updateUserIds();
- upgradeIfNecessaryLILP();
- } catch (IOException ioe) {
- fallbackToSingleUserLILP();
- } catch (XmlPullParserException pe) {
- fallbackToSingleUserLILP();
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
+ synchronized (mRestrictionsLock) {
+ mDevicePolicyGlobalUserRestrictions = newDevicePolicyGlobalUserRestrictions;
}
+ updateUserIds();
+ upgradeIfNecessaryLP();
+ } catch (IOException | XmlPullParserException e) {
+ fallbackToSingleUserLP();
+ } finally {
+ IoUtils.closeQuietly(fis);
}
}
/**
* Upgrade steps between versions, either for fixing bugs or changing the data format.
*/
- private void upgradeIfNecessaryLILP() {
+ private void upgradeIfNecessaryLP() {
+ final int originalVersion = mUserVersion;
int userVersion = mUserVersion;
if (userVersion < 1) {
// Assign a proper name for the owner, if not initialized correctly before
@@ -1132,11 +1182,14 @@
+ USER_VERSION);
} else {
mUserVersion = userVersion;
- writeUserListLILP();
+
+ if (originalVersion < mUserVersion) {
+ writeUserListLP();
+ }
}
}
- private void fallbackToSingleUserLILP() {
+ private void fallbackToSingleUserLP() {
int flags = UserInfo.FLAG_INITIALIZED;
// In split system user mode, the admin and primary flags are assigned to the first human
// user.
@@ -1161,11 +1214,14 @@
updateUserIds();
initDefaultGuestRestrictions();
- writeUserListLILP();
+ writeUserListLP();
writeUserLP(system);
}
private void scheduleWriteUser(UserInfo userInfo) {
+ if (DBG) {
+ debug("scheduleWriteUser");
+ }
// No need to wrap it within a lock -- worst case, we'll just post the same message
// twice.
if (!mHandler.hasMessages(WRITE_USER_MSG, userInfo)) {
@@ -1182,6 +1238,9 @@
* </user>
*/
private void writeUserLP(UserInfo userInfo) {
+ if (DBG) {
+ debug("writeUserLP " + userInfo);
+ }
FileOutputStream fos = null;
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + XML_SUFFIX));
try {
@@ -1221,13 +1280,12 @@
serializer.startTag(null, TAG_NAME);
serializer.text(userInfo.name);
serializer.endTag(null, TAG_NAME);
- Bundle restrictions;
synchronized (mRestrictionsLock) {
- restrictions = mBaseUserRestrictions.get(userInfo.id);
- }
- if (restrictions != null) {
- UserRestrictionsUtils
- .writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS);
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mBaseUserRestrictions.get(userInfo.id), TAG_RESTRICTIONS);
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyLocalUserRestrictions.get(userInfo.id),
+ TAG_DEVICE_POLICY_RESTRICTIONS);
}
serializer.endTag(null, TAG_USER);
@@ -1247,8 +1305,10 @@
* <user id="2"></user>
* </users>
*/
- private void writeUserListLILP() {
- // TODO Investigate removing a dependency on mInstallLock
+ private void writeUserListLP() {
+ if (DBG) {
+ debug("writeUserList");
+ }
FileOutputStream fos = null;
AtomicFile userListFile = new AtomicFile(mUserListFile);
try {
@@ -1266,9 +1326,15 @@
serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
- UserRestrictionsUtils
- .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
+ }
serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mRestrictionsLock) {
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS);
+ }
int[] userIdsToWrite;
synchronized (mUsersLock) {
userIdsToWrite = new int[mUsers.size()];
@@ -1293,7 +1359,7 @@
}
}
- private UserInfo readUserLILP(int id) {
+ private UserInfo readUserLP(int id) {
int flags = 0;
int serialNumber = id;
String name = null;
@@ -1304,7 +1370,8 @@
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
boolean partial = false;
boolean guestToRemove = false;
- Bundle restrictions = new Bundle();
+ Bundle baseRestrictions = new Bundle();
+ Bundle localRestrictions = new Bundle();
FileInputStream fis = null;
try {
@@ -1361,7 +1428,9 @@
name = parser.getText();
}
} else if (TAG_RESTRICTIONS.equals(tag)) {
- UserRestrictionsUtils.readRestrictions(parser, restrictions);
+ UserRestrictionsUtils.readRestrictions(parser, baseRestrictions);
+ } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
+ UserRestrictionsUtils.readRestrictions(parser, localRestrictions);
}
}
}
@@ -1375,7 +1444,8 @@
userInfo.profileGroupId = profileGroupId;
userInfo.restrictedProfileParentId = restrictedProfileParentId;
synchronized (mRestrictionsLock) {
- mBaseUserRestrictions.append(id, restrictions);
+ mBaseUserRestrictions.put(id, baseRestrictions);
+ mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
}
return userInfo;
@@ -1468,8 +1538,7 @@
}
private UserInfo createUserInternal(String name, int flags, int parentId) {
- if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
- UserManager.DISALLOW_ADD_USER, false)) {
+ if (hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.getCallingUserId())) {
Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
return null;
}
@@ -1480,118 +1549,114 @@
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
final long ident = Binder.clearCallingIdentity();
- UserInfo userInfo = null;
+ UserInfo userInfo;
final int userId;
try {
- synchronized (mInstallLock) {
- synchronized (mPackagesLock) {
- UserInfo parent = null;
- if (parentId != UserHandle.USER_NULL) {
- synchronized (mUsersLock) {
- parent = getUserInfoLU(parentId);
- }
- if (parent == null) return null;
+ synchronized (mPackagesLock) {
+ UserInfo parent = null;
+ if (parentId != UserHandle.USER_NULL) {
+ synchronized (mUsersLock) {
+ parent = getUserInfoLU(parentId);
}
- if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
- Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
+ if (parent == null) return null;
+ }
+ if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
+ Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
+ return null;
+ }
+ if (!isGuest && !isManagedProfile && isUserLimitReached()) {
+ // If we're not adding a guest user or a managed profile and the limit has
+ // been reached, cannot add a user.
+ return null;
+ }
+ // If we're adding a guest and there already exists one, bail.
+ if (isGuest && findCurrentGuestUser() != null) {
+ return null;
+ }
+ // In legacy mode, restricted profile's parent can only be the owner user
+ if (isRestricted && !UserManager.isSplitSystemUser()
+ && (parentId != UserHandle.USER_SYSTEM)) {
+ Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner");
+ return null;
+ }
+ if (isRestricted && UserManager.isSplitSystemUser()) {
+ if (parent == null) {
+ Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be "
+ + "specified");
return null;
}
- if (!isGuest && !isManagedProfile && isUserLimitReached()) {
- // If we're not adding a guest user or a managed profile and the limit has
- // been reached, cannot add a user.
+ if (!parent.canHaveProfile()) {
+ Log.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be "
+ + "created for the specified parent user id " + parentId);
return null;
}
- // If we're adding a guest and there already exists one, bail.
- if (isGuest && findCurrentGuestUser() != null) {
- return null;
+ }
+ // In split system user mode, we assign the first human user the primary flag.
+ // And if there is no device owner, we also assign the admin flag to primary user.
+ if (UserManager.isSplitSystemUser()
+ && !isGuest && !isManagedProfile && getPrimaryUser() == null) {
+ flags |= UserInfo.FLAG_PRIMARY;
+ DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (devicePolicyManager == null
+ || !devicePolicyManager.isDeviceManaged()) {
+ flags |= UserInfo.FLAG_ADMIN;
}
- // In legacy mode, restricted profile's parent can only be the owner user
- if (isRestricted && !UserManager.isSplitSystemUser()
- && (parentId != UserHandle.USER_SYSTEM)) {
- Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner");
- return null;
- }
- if (isRestricted && UserManager.isSplitSystemUser()) {
- if (parent == null) {
- Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be "
- + "specified");
- return null;
- }
- if (!parent.canHaveProfile()) {
- Log.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be "
- + "created for the specified parent user id " + parentId);
- return null;
- }
- }
- // In split system user mode, we assign the first human user the primary flag.
- // And if there is no device owner, we also assign the admin flag to primary
- // user.
- if (UserManager.isSplitSystemUser()
- && !isGuest && !isManagedProfile && getPrimaryUser() == null) {
- flags |= UserInfo.FLAG_PRIMARY;
- DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
- mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (devicePolicyManager == null
- || devicePolicyManager.getDeviceOwner() == null) {
- flags |= UserInfo.FLAG_ADMIN;
- }
- }
- userId = getNextAvailableId();
- userInfo = new UserInfo(userId, name, null, flags);
- userInfo.serialNumber = mNextSerialNumber++;
- long now = System.currentTimeMillis();
- userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
- userInfo.partial = true;
- Environment.getUserSystemDirectory(userInfo.id).mkdirs();
+ }
+ userId = getNextAvailableId();
+ userInfo = new UserInfo(userId, name, null, flags);
+ userInfo.serialNumber = mNextSerialNumber++;
+ long now = System.currentTimeMillis();
+ userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+ userInfo.partial = true;
+ Environment.getUserSystemDirectory(userInfo.id).mkdirs();
+ synchronized (mUsersLock) {
mUsers.put(userId, userInfo);
- writeUserListLILP();
- if (parent != null) {
- if (isManagedProfile) {
- if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
- parent.profileGroupId = parent.id;
- scheduleWriteUser(parent);
- }
- userInfo.profileGroupId = parent.profileGroupId;
- } else if (isRestricted) {
- if (!parent.canHaveProfile()) {
- Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner");
- }
- if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
- parent.restrictedProfileParentId = parent.id;
- scheduleWriteUser(parent);
- }
- userInfo.restrictedProfileParentId = parent.restrictedProfileParentId;
+ }
+ writeUserListLP();
+ if (parent != null) {
+ if (isManagedProfile) {
+ if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+ parent.profileGroupId = parent.id;
+ writeUserLP(parent);
}
- }
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
- final String volumeUuid = vol.getFsUuid();
- try {
- final File userDir = Environment.getDataUserDirectory(volumeUuid,
- userId);
- prepareUserDirectory(mContext, volumeUuid, userId);
- enforceSerialNumber(userDir, userInfo.serialNumber);
- } catch (IOException e) {
- Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
+ userInfo.profileGroupId = parent.profileGroupId;
+ } else if (isRestricted) {
+ if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
+ parent.restrictedProfileParentId = parent.id;
+ writeUserLP(parent);
}
- }
- mPm.createNewUserLILPw(userId);
- userInfo.partial = false;
- scheduleWriteUser(userInfo);
- updateUserIds();
- Bundle restrictions = new Bundle();
- synchronized (mRestrictionsLock) {
- mBaseUserRestrictions.append(userId, restrictions);
+ userInfo.restrictedProfileParentId = parent.restrictedProfileParentId;
}
}
}
- mPm.newUserCreated(userId);
- if (userInfo != null) {
- Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
- mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
- android.Manifest.permission.MANAGE_USERS);
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ storage.createUserKey(userId, userInfo.serialNumber);
+ for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+ final String volumeUuid = vol.getFsUuid();
+ try {
+ final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
+ storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber);
+ enforceSerialNumber(userDir, userInfo.serialNumber);
+ } catch (IOException e) {
+ Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
+ }
}
+ mPm.createNewUser(userId);
+ userInfo.partial = false;
+ synchronized (mPackagesLock) {
+ writeUserLP(userInfo);
+ }
+ updateUserIds();
+ Bundle restrictions = new Bundle();
+ synchronized (mRestrictionsLock) {
+ mBaseUserRestrictions.append(userId, restrictions);
+ }
+ mPm.newUserCreated(userId);
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1780,11 +1845,7 @@
// Clean up any ActivityManager state
LocalServices.getService(ActivityManagerInternal.class)
.onUserRemoved(userHandle);
- synchronized (mInstallLock) {
- synchronized (mPackagesLock) {
- removeUserStateLILP(userHandle);
- }
- }
+ removeUserState(userHandle);
}
}.start();
}
@@ -1796,11 +1857,10 @@
}
}
- private void removeUserStateLILP(final int userHandle) {
- mContext.getSystemService(StorageManager.class)
- .deleteUserKey(userHandle);
+ private void removeUserState(final int userHandle) {
+ mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
// Cleanup package manager settings
- mPm.cleanUpUserLILPw(this, userHandle);
+ mPm.cleanUpUser(this, userHandle);
// Remove this user from the list
synchronized (mUsersLock) {
@@ -1810,7 +1870,9 @@
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
userFile.delete();
// Update the user list
- writeUserListLILP();
+ synchronized (mPackagesLock) {
+ writeUserListLP();
+ }
updateUserIds();
removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
@@ -2141,21 +2203,28 @@
}
/**
+ * Called right before a user starts. This will not be called for the system user.
+ */
+ public void onBeforeStartUser(int userId) {
+ synchronized (mRestrictionsLock) {
+ applyUserRestrictionsLR(userId);
+ }
+ }
+
+ /**
* Make a note of the last started time of a user and do some cleanup.
* @param userId the user that was just foregrounded
*/
public void onUserForeground(int userId) {
- synchronized (mPackagesLock) {
- UserInfo user = getUserInfoNoChecks(userId);
- long now = System.currentTimeMillis();
- if (user == null || user.partial) {
- Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
- return;
- }
- if (now > EPOCH_PLUS_30_YEARS) {
- user.lastLoggedInTime = now;
- scheduleWriteUser(user);
- }
+ UserInfo user = getUserInfoNoChecks(userId);
+ if (user == null || user.partial) {
+ Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
+ return;
+ }
+ long now = System.currentTimeMillis();
+ if (now > EPOCH_PLUS_30_YEARS) {
+ user.lastLoggedInTime = now;
+ scheduleWriteUser(user);
}
}
@@ -2163,7 +2232,6 @@
* Returns the next available user id, filling in any holes in the ids.
* TODO: May not be a good idea to recycle ids, in case it results in confusion
* for data and battery stats collection, or unexpected cross-talk.
- * @return
*/
private int getNextAvailableId() {
synchronized (mUsersLock) {
@@ -2183,16 +2251,6 @@
}
/**
- * Create new {@code /data/user/[id]} directory and sets default
- * permissions.
- */
- public static void prepareUserDirectory(Context context, String volumeUuid, int userId) {
- final StorageManager storage = context.getSystemService(StorageManager.class);
- final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
- storage.createNewUserDir(userId, userDir);
- }
-
- /**
* Enforce that serial number stored in user directory inode matches the
* given expected value. Gracefully sets the serial number if currently
* undefined.
@@ -2349,15 +2407,26 @@
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.dumpRestrictions(
pw, " ", mBaseUserRestrictions.get(user.id));
+ pw.println(" Device policy local restrictions:");
+ UserRestrictionsUtils.dumpRestrictions(
+ pw, " ", mDevicePolicyLocalUserRestrictions.get(user.id));
pw.println(" Effective restrictions:");
UserRestrictionsUtils.dumpRestrictions(
pw, " ", mCachedEffectiveUserRestrictions.get(user.id));
}
+ pw.println();
}
}
+ pw.println(" Device policy global restrictions:");
+ synchronized (mRestrictionsLock) {
+ UserRestrictionsUtils
+ .dumpRestrictions(pw, " ", mDevicePolicyGlobalUserRestrictions);
+ }
pw.println();
- pw.println("Guest restrictions:");
- UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions);
+ pw.println(" Guest restrictions:");
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions);
+ }
}
}
@@ -2388,22 +2457,11 @@
}
private class LocalService extends UserManagerInternal {
-
@Override
- public Object getUserRestrictionsLock() {
- return mRestrictionsLock;
- }
-
- @Override
- @GuardedBy("mRestrictionsLock")
- public void updateEffectiveUserRestrictionsLR(int userId) {
- UserManagerService.this.updateEffectiveUserRestrictionsLR(userId);
- }
-
- @Override
- @GuardedBy("mRestrictionsLock")
- public void updateEffectiveUserRestrictionsForAllUsersLR() {
- UserManagerService.this.updateEffectiveUserRestrictionsForAllUsersLR();
+ public void setDevicePolicyUserRestrictions(int userId, @NonNull Bundle localRestrictions,
+ @Nullable Bundle globalRestrictions) {
+ UserManagerService.this.setDevicePolicyUserRestrictions(userId, localRestrictions,
+ globalRestrictions);
}
@Override
@@ -2468,4 +2526,9 @@
pw.println(" Prints all users on the system.");
}
}
+
+ private static void debug(String message) {
+ Log.d(LOG_TAG, message +
+ (DBG_WITH_STACKTRACE ? " called at\n" + Debug.getCallers(10, " ") : ""));
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 56e8b3e..77abd3e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -18,6 +18,10 @@
import com.google.android.collect.Sets;
+import com.android.internal.util.Preconditions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -26,6 +30,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -45,7 +50,7 @@
private UserRestrictionsUtils() {
}
- public static final String[] USER_RESTRICTIONS = {
+ public static final Set<String> USER_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_CONFIG_WIFI,
UserManager.DISALLOW_MODIFY_ACCOUNTS,
UserManager.DISALLOW_INSTALL_APPS,
@@ -79,28 +84,69 @@
UserManager.DISALLOW_SAFE_BOOT,
UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
UserManager.DISALLOW_RECORD_AUDIO,
- };
-
- /**
- * Set of user restrictions, which can only be enforced by the system.
- */
- public static final Set<String> SYSTEM_CONTROLLED_USER_RESTRICTIONS = Sets.newArraySet(
- UserManager.DISALLOW_RECORD_AUDIO);
+ UserManager.DISALLOW_CAMERA
+ );
/**
* Set of user restriction which we don't want to persist.
*/
- public static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet(
- UserManager.DISALLOW_RECORD_AUDIO);
+ private static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_RECORD_AUDIO
+ );
- public static void writeRestrictions(XmlSerializer serializer, Bundle restrictions,
- String tag) throws IOException {
+ /**
+ * User restrictions that can not be set by profile owners.
+ */
+ private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_USB_FILE_TRANSFER,
+ UserManager.DISALLOW_CONFIG_TETHERING,
+ UserManager.DISALLOW_NETWORK_RESET,
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+ UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_FUN,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.DISALLOW_CREATE_WINDOWS
+ );
+
+ /**
+ * User restrictions that can't be changed by device owner or profile owner.
+ */
+ private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
+ UserManager.DISALLOW_RECORD_AUDIO,
+ UserManager.DISALLOW_WALLPAPER
+ );
+
+ /**
+ * Special user restrictions that can be applied to a user as well as to all users globally,
+ * depending on callers. When device owner sets them, they'll be applied to all users.
+ */
+ private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE
+ );
+
+ public static void writeRestrictions(@NonNull XmlSerializer serializer,
+ @Nullable Bundle restrictions, @NonNull String tag) throws IOException {
+ if (restrictions == null) {
+ return;
+ }
+
serializer.startTag(null, tag);
- for (String key : USER_RESTRICTIONS) {
- if (restrictions.getBoolean(key)
- && !NON_PERSIST_USER_RESTRICTIONS.contains(key)) {
- serializer.attribute(null, key, "true");
+ for (String key : restrictions.keySet()) {
+ if (NON_PERSIST_USER_RESTRICTIONS.contains(key)) {
+ continue; // Don't persist.
}
+ if (USER_RESTRICTIONS.contains(key)) {
+ if (restrictions.getBoolean(key)) {
+ serializer.attribute(null, key, "true");
+ }
+ continue;
+ }
+ Log.w(TAG, "Unknown user restriction detected: " + key);
}
serializer.endTag(null, tag);
}
@@ -115,7 +161,31 @@
}
}
- public static void merge(Bundle dest, Bundle in) {
+ /**
+ * @return {@code in} itself when it's not null, or an empty bundle (which can writable).
+ */
+ public static Bundle nonNull(@Nullable Bundle in) {
+ return in != null ? in : new Bundle();
+ }
+
+ public static boolean isEmpty(@Nullable Bundle in) {
+ return (in == null) || (in.size() == 0);
+ }
+
+ /**
+ * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
+ * bundle.
+ *
+ * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+ * {@link Bundle#EMPTY})
+ */
+ public static @NonNull Bundle clone(@Nullable Bundle in) {
+ return (in != null) ? new Bundle(in) : new Bundle();
+ }
+
+ public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
+ Preconditions.checkNotNull(dest);
+ Preconditions.checkArgument(dest != in);
if (in == null) {
return;
}
@@ -127,6 +197,69 @@
}
/**
+ * @return true if a restriction is settable by device owner.
+ */
+ public static boolean canDeviceOwnerChange(String restriction) {
+ return !IMMUTABLE_BY_OWNERS.contains(restriction);
+ }
+
+ /**
+ * @return true if a restriction is settable by profile owner.
+ */
+ public static boolean canProfileOwnerChange(String restriction) {
+ return !(IMMUTABLE_BY_OWNERS.contains(restriction)
+ || DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction));
+ }
+
+ /**
+ * Takes restrictions that can be set by device owner, and sort them into what should be applied
+ * globally and what should be applied only on the current user.
+ */
+ public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global,
+ @NonNull Bundle local) {
+ if (in == null || in.size() == 0) {
+ return;
+ }
+ for (String key : in.keySet()) {
+ if (!in.getBoolean(key)) {
+ continue;
+ }
+ if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) {
+ global.putBoolean(key, true);
+ } else {
+ local.putBoolean(key, true);
+ }
+ }
+ }
+
+ /**
+ * @return true if two Bundles contain the same user restriction.
+ * A null bundle and an empty bundle are considered to be equal.
+ */
+ public static boolean areEqual(@Nullable Bundle a, @Nullable Bundle b) {
+ if (a == b) {
+ return true;
+ }
+ if (isEmpty(a)) {
+ return isEmpty(b);
+ }
+ if (isEmpty(b)) {
+ return false;
+ }
+ for (String key : a.keySet()) {
+ if (a.getBoolean(key) != b.getBoolean(key)) {
+ return false;
+ }
+ }
+ for (String key : b.keySet()) {
+ if (a.getBoolean(key) != b.getBoolean(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Takes a new use restriction set and the previous set, and apply the restrictions that have
* changed.
*
@@ -145,16 +278,24 @@
}
}
}
-
+
/**
* Apply each user restriction.
*
* <p>Note this method is called by {@link UserManagerService} while holding
* {@code mRestrictionLock}. Be aware when calling into other services, which could cause
* a deadlock.
+ *
+ * <p>See also {@link
+ * com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser},
+ * which should be in sync with this method.
*/
private static void applyUserRestrictionLR(Context context, int userId, String key,
boolean newValue) {
+ if (UserManagerService.DBG) {
+ Log.d(TAG, "Applying user restriction: userId=" + userId
+ + " key=" + key + " value=" + newValue);
+ }
// When certain restrictions are cleared, we don't update the system settings,
// because these settings are changeable on the Settings UI and we don't know the original
// value -- for example LOCATION_MODE might have been off already when the restriction was
@@ -169,7 +310,7 @@
case UserManager.DISALLOW_CONFIG_WIFI:
if (newValue) {
android.provider.Settings.Secure.putIntForUser(cr,
- android.provider.Settings.Secure
+ android.provider.Settings.Global
.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
}
break;
@@ -179,9 +320,6 @@
android.provider.Settings.Secure.LOCATION_MODE,
android.provider.Settings.Secure.LOCATION_MODE_OFF,
userId);
- android.provider.Settings.Secure.putStringForUser(cr,
- android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
- userId);
}
// Send out notifications as some clients may want to reread the
// value which actually changed due to a restriction having been
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 121ef21..ae6874f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5318,11 +5318,9 @@
}
private boolean shouldDispatchInputWhenNonInteractive() {
- if (mDisplay == null || mDisplay.getState() == Display.STATE_OFF) {
- return false;
- }
- // Send events to keyguard while the screen is on and it's showing.
- if (isKeyguardShowingAndNotOccluded()) {
+ // Send events to keyguard while the screen is on.
+ if (isKeyguardShowingAndNotOccluded() && mDisplay != null
+ && mDisplay.getState() != Display.STATE_OFF) {
return true;
}
diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index 8fc979c..cc25c8c 100644
--- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -17,6 +17,7 @@
package com.android.server.updates;
import com.android.server.EventLogTags;
+import com.android.internal.util.HexDump;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -155,7 +156,7 @@
try {
MessageDigest dgst = MessageDigest.getInstance("SHA512");
byte[] fingerprint = dgst.digest(content);
- return IntegralToString.bytesToHexString(fingerprint, false);
+ return HexDump.toHexString(fingerprint, false);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index ac79b36..97713fc 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -54,6 +54,15 @@
@Override
public void onReceive(Context context, Intent intent) {
+ // When a package is replaced we will receive two intents, one representing the
+ // removal of the old package and one representing the addition of the new
+ // package. We here ignore the intent representing the removed package to make
+ // sure we don't change WebView provider twice.
+ if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
+ && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
+ return;
+ }
+
for (String packageName : WebViewFactory.getWebViewPackageNames()) {
String webviewPackage = "package:" + packageName;
@@ -73,7 +82,8 @@
}
};
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index d713751..c246609 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -409,6 +409,7 @@
private final Region mMagnifiedBounds = new Region();
private final Region mOldMagnifiedBounds = new Region();
+ private final Region mOldAvailableBounds = new Region();
private final Path mCircularPath;
@@ -537,29 +538,39 @@
screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Region.Op.INTERSECT);
- if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
- Region bounds = Region.obtain();
- bounds.set(magnifiedBounds);
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
- bounds).sendToTarget();
+ final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(magnifiedBounds);
+ final boolean availableChanged = !mOldAvailableBounds.equals(availableBounds);
+ if (magnifiedChanged || availableChanged) {
+ if (magnifiedChanged) {
+ mWindow.setBounds(magnifiedBounds);
+ Rect dirtyRect = mTempRect1;
+ if (mFullRedrawNeeded) {
+ mFullRedrawNeeded = false;
+ dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
+ screenWidth - mDrawBorderInset,
+ screenHeight - mDrawBorderInset);
+ mWindow.invalidate(dirtyRect);
+ } else {
+ Region dirtyRegion = mTempRegion3;
+ dirtyRegion.set(magnifiedBounds);
+ dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
+ dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+ dirtyRegion.getBounds(dirtyRect);
+ mWindow.invalidate(dirtyRect);
+ }
- mWindow.setBounds(magnifiedBounds);
- Rect dirtyRect = mTempRect1;
- if (mFullRedrawNeeded) {
- mFullRedrawNeeded = false;
- dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
- screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset);
- mWindow.invalidate(dirtyRect);
- } else {
- Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(magnifiedBounds);
- dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
- dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
- dirtyRegion.getBounds(dirtyRect);
- mWindow.invalidate(dirtyRect);
+ mOldMagnifiedBounds.set(magnifiedBounds);
}
- mOldMagnifiedBounds.set(magnifiedBounds);
+ if (availableChanged) {
+ mOldAvailableBounds.set(availableBounds);
+ }
+
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = Region.obtain(magnifiedBounds);
+ args.arg2 = Region.obtain(availableBounds);
+ mHandler.obtainMessage(
+ MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
}
}
@@ -867,9 +878,12 @@
public void handleMessage(Message message) {
switch (message.what) {
case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
- Region bounds = (Region) message.obj;
- mCallbacks.onMagnifedBoundsChanged(bounds);
- bounds.recycle();
+ final SomeArgs args = (SomeArgs) message.obj;
+ final Region magnifiedBounds = (Region) args.arg1;
+ final Region availableBounds = (Region) args.arg2;
+ mCallbacks.onMagnifiedBoundsChanged(magnifiedBounds, availableBounds);
+ magnifiedBounds.recycle();
+ availableBounds.recycle();
} break;
case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4852f02..d394125 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -139,7 +139,7 @@
private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
private final Context mContext;
- private final Handler mH;
+ private final WindowManagerService mService;
private int mNextAppTransition = TRANSIT_UNSET;
@@ -208,15 +208,10 @@
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
- private final Object mServiceLock;
- private final WindowSurfacePlacer mWindowSurfacePlacer;
- AppTransition(Context context, Handler h, Object serviceLock,
- WindowSurfacePlacer windowSurfacePlacer) {
+ AppTransition(Context context, WindowManagerService service) {
mContext = context;
- mH = h;
- mServiceLock = serviceLock;
- mWindowSurfacePlacer = windowSurfacePlacer;
+ mService = service;
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -344,6 +339,9 @@
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
+ mNextAppTransitionAnimationsSpecsFuture = null;
+ mDefaultNextAppTransitionAnimationSpec = null;
+ mAnimationFinishedCallback = null;
}
void freeze() {
@@ -971,7 +969,7 @@
@Override
public void onAnimationEnd(Animation animation) {
- mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
+ mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
}
@Override
@@ -1326,7 +1324,8 @@
void postAnimationCallback() {
if (mNextAppTransitionCallback != null) {
- mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
+ mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
+ mNextAppTransitionCallback));
mNextAppTransitionCallback = null;
}
}
@@ -1334,14 +1333,13 @@
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- mAnimationFinishedCallback = null;
} else {
postAnimationCallback();
}
@@ -1350,40 +1348,34 @@
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
startY + startHeight, null);
postAnimationCallback();
- mNextAppTransitionCallback = null;
- mAnimationFinishedCallback = null;
}
}
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
postAnimationCallback();
- mNextAppTransitionCallback = null;
- mAnimationFinishedCallback = null;
}
}
void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- mAnimationFinishedCallback = null;
} else {
postAnimationCallback();
}
@@ -1392,16 +1384,14 @@
void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
srcThumb);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
- mAnimationFinishedCallback = null;
} else {
postAnimationCallback();
}
@@ -1411,22 +1401,22 @@
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
boolean scaleUp) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mDefaultNextAppTransitionAnimationSpec = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- for (int i = 0; i < specs.length; i++) {
- AppTransitionAnimationSpec spec = specs[i];
- if (spec != null) {
- mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
- if (i == 0) {
- // In full screen mode, the transition code depends on the default spec to
- // be set.
- Rect rect = spec.rect;
- putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
- rect.height(), null);
+ if (specs != null) {
+ for (int i = 0; i < specs.length; i++) {
+ AppTransitionAnimationSpec spec = specs[i];
+ if (spec != null) {
+ mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
+ if (i == 0) {
+ // In full screen mode, the transition code depends on the default spec
+ // to be set.
+ Rect rect = spec.rect;
+ putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
+ rect.width(), rect.height(), spec.bitmap);
+ }
}
}
}
@@ -1442,11 +1432,9 @@
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
- mNextAppTransitionPackage = null;
- mDefaultNextAppTransitionAnimationSpec = null;
- mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionAnimationsSpecsFuture = specsFuture;
mNextAppTransitionScaleUp = scaleUp;
mNextAppTransitionFutureCallback = callback;
@@ -1455,10 +1443,10 @@
void overrideInPlaceAppTransition(String packageName, int anim) {
if (isTransitionSet()) {
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
- mAnimationFinishedCallback = null;
} else {
postAnimationCallback();
}
@@ -1482,14 +1470,17 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to fetch app transition specs: " + e);
}
- synchronized (mServiceLock) {
+ synchronized (mService.mWindowMap) {
mNextAppTransitionAnimationsSpecsPending = false;
overridePendingAppTransitionMultiThumb(specs,
mNextAppTransitionFutureCallback, null /* finishedCallback */,
mNextAppTransitionScaleUp);
mNextAppTransitionFutureCallback = null;
- mWindowSurfacePlacer.requestTraversal();
+ if (specs != null) {
+ mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
+ }
}
+ mService.requestTraversal();
}
});
}
@@ -1676,8 +1667,8 @@
}
boolean prepared = prepare();
if (isTransitionSet()) {
- mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
+ mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+ mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
}
return prepared;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2905269..b32ec2d 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -37,6 +37,10 @@
public class AppWindowAnimator {
static final String TAG = "AppWindowAnimator";
+ private static final int PROLONG_ANIMATION_DISABLED = 0;
+ static final int PROLONG_ANIMATION_AT_END = 1;
+ static final int PROLONG_ANIMATION_AT_START = 2;
+
final AppWindowToken mAppToken;
final WindowManagerService mService;
final WindowAnimator mAnimator;
@@ -85,7 +89,7 @@
// If true when the animation hits the last frame, it will keep running on that last frame.
// This is used to synchronize animation with Recents and we wait for Recents to tell us to
// finish or for a new animation be set as fail-safe mechanism.
- private boolean mProlongAnimation;
+ private int mProlongAnimation;
// Whether the prolong animation can be removed when animation is set. The purpose of this is
// that if recents doesn't tell us to remove the prolonged animation, we will get rid of it
// when new animation is set.
@@ -142,7 +146,7 @@
anim.setBackgroundColor(0);
}
if (mClearProlongedAnimation) {
- mProlongAnimation = false;
+ mProlongAnimation = PROLONG_ANIMATION_DISABLED;
} else {
mClearProlongedAnimation = true;
}
@@ -224,7 +228,8 @@
private void stepThumbnailAnimation(long currentTime) {
thumbnailTransformation.clear();
- thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+ final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
+ thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
ScreenRotationAnimation screenRotationAnimation =
@@ -261,12 +266,26 @@
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
}
+ /**
+ * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
+ * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
+ * and keep producing the first frame of the animation.
+ */
+ private long getAnimationFrameTime(Animation animation, long currentTime) {
+ if (mProlongAnimation == PROLONG_ANIMATION_AT_START) {
+ animation.setStartTime(currentTime);
+ return currentTime + 1;
+ }
+ return currentTime;
+ }
+
private boolean stepAnimation(long currentTime) {
if (animation == null) {
return false;
}
transformation.clear();
- boolean hasMoreFrames = animation.getTransformation(currentTime, transformation);
+ final long animationFrameTime = getAnimationFrameTime(animation, currentTime);
+ boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation);
if (!hasMoreFrames) {
if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
// We are deferring the thumbnail destruction, so extend the animation for one more
@@ -278,14 +297,14 @@
"Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
deferFinalFrameCleanup = false;
- if (mProlongAnimation) {
+ if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
hasMoreFrames = true;
} else {
animation = null;
+ clearThumbnail();
+ if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
+ + currentTime);
}
- clearThumbnail();
- if (DEBUG_ANIM) Slog.v(TAG,
- "Finished animation in " + mAppToken + " @ " + currentTime);
}
}
hasTransformation = hasMoreFrames;
@@ -434,13 +453,13 @@
}
}
- void startProlongAnimation() {
- mProlongAnimation = true;
+ void startProlongAnimation(int prolongType) {
+ mProlongAnimation = prolongType;
mClearProlongedAnimation = false;
}
void endProlongedAnimation() {
- mProlongAnimation = false;
+ mProlongAnimation = PROLONG_ANIMATION_DISABLED;
}
// This is an animation that does nothing: it just immediately finishes
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 85a12db..4b3620f 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -70,7 +70,7 @@
/** Returns the display info. of the dim layer user. */
DisplayInfo getDisplayInfo();
/** Gets the bounds of the dim layer user. */
- void getBounds(Rect outBounds);
+ void getDimBounds(Rect outBounds);
String toShortString();
}
/** The user of this dim layer. */
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 921d27c..bd30bd5 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -43,7 +43,7 @@
/** Updates the dim layer bounds, recreating it if needed. */
void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = getOrCreateDimLayerState(dimLayerUser, false);
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
final boolean previousFullscreen = state.dimLayer != null
&& state.dimLayer == mSharedFullScreenDimLayer;
DimLayer newDimLayer;
@@ -63,7 +63,7 @@
// Create new full screen dim layer.
newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
}
- dimLayerUser.getBounds(mTmpBounds);
+ dimLayerUser.getDimBounds(mTmpBounds);
newDimLayer.setBounds(mTmpBounds);
mSharedFullScreenDimLayer = newDimLayer;
} else if (state.dimLayer != null) {
@@ -73,14 +73,13 @@
newDimLayer = (state.dimLayer == null || previousFullscreen)
? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
: state.dimLayer;
- dimLayerUser.getBounds(mTmpBounds);
+ dimLayerUser.getDimBounds(mTmpBounds);
newDimLayer.setBounds(mTmpBounds);
}
state.dimLayer = newDimLayer;
}
- private DimLayerState getOrCreateDimLayerState(
- DimLayer.DimLayerUser dimLayerUser, boolean aboveApp) {
+ private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
+ dimLayerUser.toShortString());
DimLayerState state = mState.get(dimLayerUser);
@@ -88,7 +87,6 @@
state = new DimLayerState();
mState.put(dimLayerUser, state);
}
- state.dimAbove = aboveApp;
return state;
}
@@ -127,7 +125,8 @@
WindowStateAnimator newWinAnimator, boolean aboveApp) {
// Only set dim params on the highest dimmed layer.
// Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimLayerState state = getOrCreateDimLayerState(dimLayerUser, aboveApp);
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
+ state.dimAbove = aboveApp;
if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
+ " dimLayerUser=" + dimLayerUser.toShortString()
+ " newWinAnimator=" + newWinAnimator
@@ -161,7 +160,7 @@
+ " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
if (!state.continueDimming && state.dimLayer.isDimming()) {
state.animator = null;
- dimLayerUser.getBounds(mTmpBounds);
+ dimLayerUser.getDimBounds(mTmpBounds);
state.dimLayer.setBounds(mTmpBounds);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8c00a57..e9e09ec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -22,7 +22,6 @@
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import android.app.ActivityManager.StackId;
import android.graphics.Rect;
@@ -55,17 +54,6 @@
* from mDisplayWindows; */
private final WindowList mWindows = new WindowList();
- // This protects the following display size properties, so that
- // getDisplaySize() doesn't need to acquire the global lock. This is
- // needed because the window manager sometimes needs to use ActivityThread
- // while it has its global state locked (for example to load animation
- // resources), but the ActivityThread also needs get the current display
- // size sometimes when it has its package lock held.
- //
- // These will only be modified with both mWindowMap and mDisplaySizeLock
- // held (in that order) so the window manager doesn't need to acquire this
- // lock when needing these values in its normal operation.
- final Object mDisplaySizeLock = new Object();
int mInitialDisplayWidth = 0;
int mInitialDisplayHeight = 0;
int mInitialDisplayDensity = 0;
@@ -102,6 +90,9 @@
/** Detect user tapping outside of current focused stack bounds .*/
Region mTouchExcludeRegion = new Region();
+ /** Detect user tapping in a non-resizeable task in docked or fullscreen stack .*/
+ Region mNonResizeableRegion = new Region();
+
/** Save allocating when calculating rects */
private Rect mTmpRect = new Rect();
private Rect mTmpRect2 = new Rect();
@@ -202,18 +193,16 @@
}
void initializeDisplayBaseInfo() {
- synchronized(mDisplaySizeLock) {
- // Bootstrap the default logical display from the display manager.
- final DisplayInfo newDisplayInfo =
- mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
- if (newDisplayInfo != null) {
- mDisplayInfo.copyFrom(newDisplayInfo);
- }
- mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
- mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
- mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
- mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+ // Bootstrap the default logical display from the display manager.
+ final DisplayInfo newDisplayInfo =
+ mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+ if (newDisplayInfo != null) {
+ mDisplayInfo.copyFrom(newDisplayInfo);
}
+ mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
+ mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
+ mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+ mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
}
void getLogicalDisplayRect(Rect out) {
@@ -288,19 +277,26 @@
int taskIdFromPoint(int x, int y) {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+ TaskStack stack = mStacks.get(stackNdx);
+ stack.getBounds(mTmpRect);
+ if (!mTmpRect.contains(x, y)) {
+ continue;
+ }
+ final ArrayList<Task> tasks = stack.getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
- // We need to use the visible frame on the window for any touch-related tests.
- // Can't use the task's bounds because the original task bounds might be adjusted
- // to fit the content frame. For example, the presence of the IME adjusting the
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. For example, the presence of the IME adjusting the
// windows frames when the app window is the IME target.
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
- if (mTmpRect.contains(x, y)) {
- return task.mTaskId;
- }
+ task.getDimBounds(mTmpRect);
+ if (mTmpRect.contains(x, y)) {
+ return task.mTaskId;
}
}
}
@@ -308,10 +304,10 @@
}
/**
- * Find the window whose outside touch area (for resizing) (x, y) falls within.
+ * Find the task whose outside touch area (for resizing) (x, y) falls within.
* Returns null if the touch doesn't fall into a resizing area.
*/
- WindowState findWindowForControlPoint(int x, int y) {
+ Task findTaskForControlPoint(int x, int y) {
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
@@ -325,24 +321,22 @@
return null;
}
- // We need to use the visible frame on the window for any touch-related
- // tests. Can't use the task's bounds because the original task bounds
- // might be adjusted to fit the content frame. (One example is when the
- // task is put to top-left quadrant, the actual visible frame would not
- // start at (0,0) after it's adjusted for the status bar.)
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
- mTmpRect.inset(-delta, -delta);
- if (mTmpRect.contains(x, y)) {
- mTmpRect.inset(delta, delta);
- if (!mTmpRect.contains(x, y)) {
- return win;
- }
- // User touched inside the task. No need to look further,
- // focus transfer will be handled in ACTION_UP.
- return null;
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. One example is when the task is put to top-left quadrant,
+ // the actual visible area would not start at (0,0) after it's adjusted
+ // for the status bar.
+ task.getDimBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return task;
}
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return null;
}
}
}
@@ -351,12 +345,19 @@
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
- WindowList windows = getWindowList();
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
- for (int i = windows.size() - 1; i >= 0; --i) {
- final WindowState win = windows.get(i);
- final Task task = win.getTask();
- if (win.isVisibleLw() && task != null) {
+ boolean addBackFocusedTask = false;
+ mNonResizeableRegion.setEmpty();
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ TaskStack stack = mStacks.get(stackNdx);
+ final ArrayList<Task> tasks = stack.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+
/**
* Exclusion region is the region that TapDetector doesn't care about.
* Here we want to remove all non-focused tasks from the exclusion region.
@@ -368,13 +369,17 @@
*/
final boolean isFreeformed = task.inFreeformWorkspace();
if (task != focusedTask || isFreeformed) {
- mTmpRect.set(win.mVisibleFrame);
- mTmpRect.intersect(win.mVisibleInsets);
- /**
- * If the task is freeformed, enlarge the area to account for outside
- * touch area for resize.
- */
+ task.getDimBounds(mTmpRect);
if (isFreeformed) {
+ // If we're removing a freeform, focused app from the exclusion region,
+ // we need to add back its touchable frame later. Remember the touchable
+ // frame now.
+ if (task == focusedTask) {
+ addBackFocusedTask = true;
+ mTmpRect2.set(mTmpRect);
+ }
+ // If the task is freeformed, enlarge the area to account for outside
+ // touch area for resize.
mTmpRect.inset(-delta, -delta);
// Intersect with display content rect. If we have system decor (status bar/
// navigation bar), we want to exclude that from the tap detection.
@@ -385,19 +390,21 @@
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
- /**
- * If we removed the focused task above, add it back and only leave its
- * outside touch area in the exclusion. TapDectector is not interested in
- * any touch inside the focused task itself.
- */
- if (task == focusedTask && isFreeformed) {
- mTmpRect.inset(delta, delta);
- mTouchExcludeRegion.op(mTmpRect, Region.Op.UNION);
+ if (task.isDockedInEffect() && !task.isResizeable()) {
+ stack.getBounds(mTmpRect);
+ mNonResizeableRegion.op(mTmpRect, Region.Op.UNION);
+ break;
}
}
}
+ // If we removed the focused task above, add it back and only leave its
+ // outside touch area in the exclusion. TapDectector is not interested in
+ // any touch inside the focused task itself.
+ if (addBackFocusedTask) {
+ mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
+ }
if (mTapDetector != null) {
- mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
+ mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index c1e0481..6b62467 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -87,7 +87,7 @@
frame.set(mLastRect);
return;
} else {
- stack.getBounds(mTmpRect);
+ stack.getDimBounds(mTmpRect);
}
int side = stack.getDockSide();
switch (side) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 400cc5e..0ef0e58 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -16,7 +16,15 @@
package com.android.server.wm;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+import android.graphics.Matrix;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateAnimation;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
@@ -46,6 +54,8 @@
* Drag/drop state
*/
class DragState {
+ private static final long ANIMATION_DURATION_MS = 500;
+
final WindowManagerService mService;
IBinder mToken;
SurfaceControl mSurfaceControl;
@@ -56,6 +66,8 @@
ClipData mData;
ClipDescription mDataDescription;
boolean mDragResult;
+ float mOriginalAlpha;
+ float mOriginalX, mOriginalY;
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
@@ -70,6 +82,10 @@
private final Region mTmpRegion = new Region();
private final Rect mTmpRect = new Rect();
+ private Animation mAnimation;
+ final Transformation mTransformation = new Transformation();
+ private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
+
DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
int flags, IBinder localWin) {
mService = service;
@@ -185,6 +201,9 @@
/* call out to each visible window/session informing it about the drag
*/
void broadcastDragStartedLw(final float touchX, final float touchY) {
+ mOriginalX = mCurrentX = touchX;
+ mOriginalY = mCurrentY = touchY;
+
// Cache a base-class instance of the clip metadata so that parceling
// works correctly in calling out to the apps.
mDataDescription = (mData != null) ? mData.getDescription() : null;
@@ -263,7 +282,7 @@
}
}
- void broadcastDragEndedLw() {
+ private void broadcastDragEndedLw() {
final int myPid = Process.myPid();
if (WindowManagerService.DEBUG_DRAG) {
@@ -295,19 +314,45 @@
}
void endDragLw() {
- mService.mDragState.broadcastDragEndedLw();
+ if (mAnimation != null) {
+ return;
+ }
+ if (!mDragResult) {
+ mAnimation = createReturnAnimationLocked();
+ mService.scheduleAnimationLocked();
+ return; // Will call cleanUpDragLw when the animation is done.
+ }
+ cleanUpDragLw();
+ }
+
+ void cancelDragLw() {
+ if (mAnimation != null) {
+ return;
+ }
+ mAnimation = createCancelAnimationLocked();
+ mService.scheduleAnimationLocked();
+ }
+
+ private void cleanUpDragLw() {
+ broadcastDragEndedLw();
// stop intercepting input
- mService.mDragState.unregister();
+ unregister();
// free our resources and drop all the object references
- mService.mDragState.reset();
+ reset();
mService.mDragState = null;
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
void notifyMoveLw(float x, float y) {
+ if (mAnimation != null) {
+ return;
+ }
+ mCurrentX = x;
+ mCurrentY = y;
+
final int myPid = Process.myPid();
// Move the surface to the given touch
@@ -379,6 +424,12 @@
// result from the recipient.
boolean notifyDropLw(WindowState touchedWin, DropPermissionHolder dropPermissionHolder,
float x, float y) {
+ if (mAnimation != null) {
+ return false;
+ }
+ mCurrentX = x;
+ mCurrentY = y;
+
if (touchedWin == null) {
// "drop" outside a valid window -- no recipient to apply a
// timeout to, and we can send the drag-ended message immediately.
@@ -435,7 +486,7 @@
continue;
}
- child.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ child.getVisibleBounds(mTmpRect);
if (!mTmpRect.contains(x, y)) {
// outside of this window's activity stack == don't tell about drags
continue;
@@ -470,4 +521,49 @@
return DragEvent.obtain(action, winX, winY, localState, description, data,
dropPermissionHolder, result);
}
+
+ boolean stepAnimationLocked(long currentTimeMs) {
+ if (mAnimation == null) {
+ return false;
+ }
+
+ mTransformation.clear();
+ if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) {
+ cleanUpDragLw();
+ return false;
+ }
+
+ mTransformation.getMatrix().postTranslate(
+ mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY);
+ final float tmpFloats[] = mService.mTmpFloats;
+ mTransformation.getMatrix().getValues(tmpFloats);
+ mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+ mSurfaceControl.setAlpha(mTransformation.getAlpha());
+ mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+ tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ return true;
+ }
+
+ private Animation createReturnAnimationLocked() {
+ final AnimationSet set = new AnimationSet(false);
+ set.addAnimation(new TranslateAnimation(
+ 0, mOriginalX - mCurrentX, 0, mOriginalY - mCurrentY));
+ set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
+ set.setDuration(ANIMATION_DURATION_MS);
+ set.setInterpolator(mCubicEaseOutInterpolator);
+ set.initialize(0, 0, 0, 0);
+ set.start(); // Will start on the first call to getTransformation.
+ return set;
+ }
+
+ private Animation createCancelAnimationLocked() {
+ final AnimationSet set = new AnimationSet(false);
+ set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
+ set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
+ set.setDuration(ANIMATION_DURATION_MS);
+ set.setInterpolator(mCubicEaseOutInterpolator);
+ set.initialize(0, 0, 0, 0);
+ set.start(); // Will start on the first call to getTransformation.
+ return set;
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 3c3123f..5511136 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -171,10 +171,10 @@
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean hasFocus, final boolean hasWallpaper, DisplayContent displayContent) {
+ final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
- flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags, this);
+ flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
@@ -194,6 +194,14 @@
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ if (child.isDockedInEffect()) {
+ // Adjust to account for non-resizeable tasks that's scrolled
+ inputWindowHandle.frameLeft += child.mXOffset;
+ inputWindowHandle.frameTop += child.mYOffset;
+ inputWindowHandle.frameRight += child.mXOffset;
+ inputWindowHandle.frameBottom += child.mYOffset;
+ }
+
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
@@ -204,7 +212,8 @@
}
if (DEBUG_INPUT) {
- Slog.d(WindowManagerService.TAG, "addInputWindowHandle: " + inputWindowHandle);
+ Slog.d(WindowManagerService.TAG, "addInputWindowHandle: "
+ + child + ", " + inputWindowHandle);
}
addInputWindowHandleLw(inputWindowHandle);
}
@@ -308,8 +317,8 @@
mService.mDragState.sendDragStartedIfNeededLw(child);
}
- addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
- hasWallpaper, displayContent);
+ addInputWindowHandleLw(
+ inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1caeca0..6e2e830 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -325,8 +325,6 @@
}
mService.mDragState.mData = data;
- mService.mDragState.mCurrentX = touchX;
- mService.mDragState.mCurrentY = touchY;
mService.mDragState.broadcastDragStartedLw(touchX, touchY);
// remember the thumb offsets for later
@@ -401,6 +399,34 @@
}
}
+ public void cancelDragAndDrop(IBinder dragToken) {
+ if (WindowManagerService.DEBUG_DRAG) {
+ Slog.d(WindowManagerService.TAG, "cancelDragAndDrop");
+ }
+
+ synchronized (mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (mService.mDragState == null) {
+ Slog.w(WindowManagerService.TAG, "cancelDragAndDrop() without prepareDrag()");
+ throw new IllegalStateException("cancelDragAndDrop() without prepareDrag()");
+ }
+
+ if (mService.mDragState.mToken != dragToken) {
+ Slog.w(WindowManagerService.TAG,
+ "cancelDragAndDrop() does not match prepareDrag()");
+ throw new IllegalStateException(
+ "cancelDragAndDrop() does not match prepareDrag()");
+ }
+
+ mService.mDragState.mDragResult = false;
+ mService.mDragState.cancelDragLw();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public void dragRecipientEntered(IWindow window) {
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "Drag into new candidate view @ " + window.asBinder());
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6aaf3c7..74e8e53 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
@@ -72,6 +73,9 @@
// For handling display rotations.
private Rect mTmpRect2 = new Rect();
+ // Whether the task is resizeable
+ private boolean mResizeable;
+
// Whether the task is currently being drag-resized
private boolean mDragResizing;
@@ -135,7 +139,7 @@
stack.addTask(this, toTop);
}
- void positionTaskInStack(TaskStack stack, int position) {
+ void positionTaskInStack(TaskStack stack, int position, Rect bounds, Configuration config) {
if (mStack != null && stack != mStack) {
if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
+ " from stack=" + mStack);
@@ -143,6 +147,7 @@
mStack.removeTask(this);
}
stack.positionTask(this, position, showForAllUsers());
+ setBounds(bounds, config);
}
boolean removeAppToken(AppWindowToken wtoken) {
@@ -189,14 +194,6 @@
bounds = mTmpRect;
mFullscreen = true;
} else {
- if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
- && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
- // ensure bounds are entirely within the display rect
- if (!bounds.intersect(mTmpRect)) {
- // Can't set bounds outside the containing display...Sorry!
- return BOUNDS_CHANGE_NONE;
- }
- }
mFullscreen = mTmpRect.equals(bounds);
}
}
@@ -226,6 +223,14 @@
return boundsChange;
}
+ void setResizeable(boolean resizeable) {
+ mResizeable = resizeable;
+ }
+
+ boolean isResizeable() {
+ return mResizeable;
+ }
+
boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
int boundsChanged = setBounds(bounds, configuration);
if (forced) {
@@ -240,6 +245,45 @@
return true;
}
+ boolean scrollLocked(Rect bounds) {
+ // shift the task bound if it doesn't fully cover the stack area
+ mStack.getDimBounds(mTmpRect);
+ if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
+ if (bounds.left > mTmpRect.left) {
+ bounds.left = mTmpRect.left;
+ bounds.right = mTmpRect.left + mBounds.width();
+ } else if (bounds.right < mTmpRect.right) {
+ bounds.left = mTmpRect.right - mBounds.width();
+ bounds.right = mTmpRect.right;
+ }
+ } else {
+ if (bounds.top > mTmpRect.top) {
+ bounds.top = mTmpRect.top;
+ bounds.bottom = mTmpRect.top + mBounds.height();
+ } else if (bounds.bottom < mTmpRect.bottom) {
+ bounds.top = mTmpRect.bottom - mBounds.height();
+ bounds.bottom = mTmpRect.bottom;
+ }
+ }
+
+ if (bounds.equals(mBounds)) {
+ return false;
+ }
+ // Normal setBounds() does not allow non-null bounds for fullscreen apps.
+ // We only change bounds for the scrolling case without change it size,
+ // on resizing path we should still want the validation.
+ mBounds.set(bounds);
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ win.mXOffset = bounds.left;
+ win.mYOffset = bounds.top;
+ }
+ }
+ return true;
+ }
+
/** Return true if the current bound can get outputted to the rest of the system as-is. */
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
@@ -252,8 +296,7 @@
return false;
}
- /** Bounds of the task with other system factors taken into consideration. */
- @Override
+ /** Original bounds of the task if applicable, otherwise fullscreen rect. */
public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -268,6 +311,70 @@
mStack.getDisplayContent().getLogicalDisplayRect(out);
}
+
+ /**
+ * Calculate the maximum visible area of this task. If the task has only one app,
+ * the result will be visible frame of that app. If the task has more than one apps,
+ * we search from top down if the next app got different visible area.
+ *
+ * This effort is to handle the case where some task (eg. GMail composer) might pop up
+ * a dialog that's different in size from the activity below, in which case we should
+ * be dimming the entire task area behind the dialog.
+ *
+ * @param out Rect containing the max visible bounds.
+ * @return true if the task has some visible app windows; false otherwise.
+ */
+ boolean getMaxVisibleBounds(Rect out) {
+ boolean foundTop = false;
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
+ continue;
+ }
+ final WindowState win = token.findMainWindow();
+ if (win == null) {
+ continue;
+ }
+ if (!foundTop) {
+ out.set(win.mVisibleFrame);
+ foundTop = true;
+ continue;
+ }
+ if (win.mVisibleFrame.left < out.left) {
+ out.left = win.mVisibleFrame.left;
+ }
+ if (win.mVisibleFrame.top < out.top) {
+ out.top = win.mVisibleFrame.top;
+ }
+ if (win.mVisibleFrame.right > out.right) {
+ out.right = win.mVisibleFrame.right;
+ }
+ if (win.mVisibleFrame.bottom > out.bottom) {
+ out.bottom = win.mVisibleFrame.bottom;
+ }
+ }
+ return foundTop;
+ }
+
+ /** Bounds of the task to be used for dimming, as well as touch related tests. */
+ @Override
+ public void getDimBounds(Rect out) {
+ if (useCurrentBounds()) {
+ if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
+ return;
+ }
+
+ out.set(mBounds);
+ return;
+ }
+
+ // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+ // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+ // system.
+ mStack.getDisplayContent().getLogicalDisplayRect(out);
+ }
+
void setDragResizing(boolean dragResizing) {
mDragResizing = dragResizing;
}
@@ -354,14 +461,33 @@
return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
}
- WindowState getTopAppMainWindow() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
+ boolean isResizeableByDockedStack() {
+ return mStack != null && getDisplayContent().getDockedStackLocked() != null &&
+ StackId.isTaskResizeableByDockedStack(mStack.mStackId);
}
- AppWindowToken getTopAppWindowToken() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+ /**
+ * Whether the task should be treated as if it's docked. Returns true if the task
+ * is currently in docked workspace, or it's side-by-side to a docked task.
+ */
+ boolean isDockedInEffect() {
+ return inDockedWorkspace() || isResizeableByDockedStack();
+ }
+
+ WindowState getTopVisibleAppMainWindow() {
+ final AppWindowToken token = getTopVisibleAppToken();
+ return token != null ? token.findMainWindow() : null;
+ }
+
+ AppWindowToken getTopVisibleAppToken() {
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
+ return token;
+ }
+ }
+ return null;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index aae3bd2..32c3205 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -149,7 +149,7 @@
}
synchronized (mService.mWindowMap) {
mDragEnded = notifyMoveLocked(newX, newY);
- mTask.getBounds(mTmpRect);
+ mTask.getDimBounds(mTmpRect);
}
if (!mTmpRect.equals(mWindowDragBounds)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
@@ -196,7 +196,8 @@
? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
: DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
mService.mActivityManager.moveTaskToDockedStack(
- mTask.mTaskId, createMode, true /*toTop*/);
+ mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
+ null /* initialBounds */);
}
} catch(RemoteException e) {}
@@ -332,28 +333,42 @@
+ ", {" + startX + ", " + startY + "}");
}
mCtrlType = CTRL_NONE;
+ mTask = win.getTask();
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ if (mTask.isDockedInEffect()) {
+ // If this is a docked task or if task size is affected by docked stack changing size,
+ // we can only be here if the task is not resizeable and we're handling a two-finger
+ // scrolling. Use the original task bounds to position the task, the dim bounds
+ // is cropped and doesn't move.
+ mTask.getBounds(mTmpRect);
+ } else {
+ // Use the dim bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ // Also, use the dim bounds of the task which accounts for
+ // multiple app windows. Don't use any bounds from win itself as it
+ // may not be the same size as the task.
+ mTask.getDimBounds(mTmpRect);
+ }
+
if (resize) {
- final Rect visibleFrame = win.mVisibleFrame;
- if (startX < visibleFrame.left) {
+ if (startX < mTmpRect.left) {
mCtrlType |= CTRL_LEFT;
}
- if (startX > visibleFrame.right) {
+ if (startX > mTmpRect.right) {
mCtrlType |= CTRL_RIGHT;
}
- if (startY < visibleFrame.top) {
+ if (startY < mTmpRect.top) {
mCtrlType |= CTRL_TOP;
}
- if (startY > visibleFrame.bottom) {
+ if (startY > mTmpRect.bottom) {
mCtrlType |= CTRL_BOTTOM;
}
mResizing = true;
}
- mTask = win.getTask();
- mStartDragX = startX;
- mStartDragY = startY;
-
- mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
+ mWindowOriginalBounds.set(mTmpRect);
}
private void endDragLocked() {
@@ -364,7 +379,7 @@
/** Returns true if the move operation should be ended. */
private boolean notifyMoveLocked(float x, float y) {
if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
+ Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
}
if (mCtrlType != CTRL_NONE) {
@@ -393,16 +408,46 @@
}
// This is a moving operation.
- mTask.mStack.getBounds(mTmpRect);
- mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
- if (!mTmpRect.contains((int) x, (int) y)) {
- // We end the moving operation if position is outside the stack bounds.
- return true;
+ mTask.mStack.getDimBounds(mTmpRect);
+
+ // If this is a non-resizeable task put into side-by-side mode, we are
+ // handling a two-finger scrolling action. No need to shrink the bounds.
+ if (!mTask.isDockedInEffect()) {
+ mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
}
+
+ boolean dragEnded = false;
+ final int nX = (int) x;
+ final int nY = (int) y;
+ if (!mTmpRect.contains(nX, nY)) {
+ // We end the moving operation if position is outside the stack bounds.
+ // In this case we need to clamp the position to stack bounds and calculate
+ // the final window drag bounds.
+ x = Math.min(Math.max(x, mTmpRect.left), mTmpRect.right);
+ y = Math.min(Math.max(y, mTmpRect.top), mTmpRect.bottom);
+ dragEnded = true;
+ }
+
+ updateWindowDragBounds(nX, nY);
+ updateDimLayerVisibility(nX);
+ return dragEnded;
+ }
+
+ private void updateWindowDragBounds(int x, int y) {
mWindowDragBounds.set(mWindowOriginalBounds);
- mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
- updateDimLayerVisibility((int) x);
- return false;
+ if (mTask.isDockedInEffect()) {
+ // Offset the bounds without clamp, the bounds will be shifted later
+ // by window manager before applying the scrolling.
+ if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), 0);
+ } else {
+ mWindowDragBounds.offset(0, Math.round(y - mStartDragY));
+ }
+ } else {
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+ }
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
+ "updateWindowDragBounds: " + mWindowDragBounds);
}
private void updateDimLayerVisibility(int x) {
@@ -438,7 +483,7 @@
return CTRL_NONE;
}
- mTask.mStack.getBounds(mTmpRect);
+ mTask.mStack.getDimBounds(mTmpRect);
if (x - mSideMargin <= mTmpRect.left) {
return CTRL_LEFT;
}
@@ -450,7 +495,7 @@
}
private void showDimLayer() {
- mTask.mStack.getBounds(mTmpRect);
+ mTask.mStack.getDimBounds(mTmpRect);
if (mCurrentDimSide == CTRL_LEFT) {
mTmpRect.right = mTmpRect.centerX();
} else if (mCurrentDimSide == CTRL_RIGHT) {
@@ -473,7 +518,7 @@
}
@Override
- public void getBounds(Rect out) {
+ public void getDimBounds(Rect out) {
// This dim layer user doesn't need this.
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7c02b43..87deaa4 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -125,7 +125,18 @@
Configuration config = configs.get(task.mTaskId);
if (config != null) {
Rect bounds = taskBounds.get(task.mTaskId);
- task.setBounds(bounds, config);
+ if (!task.isResizeable() && task.isDockedInEffect()) {
+ // This is a non-resizeable task that's docked (or side-by-side to the docked
+ // stack). It might have been scrolled previously, and after the stack resizing,
+ // it might no longer fully cover the stack area.
+ // Save the old bounds and re-apply the scroll. This adjusts the bounds to
+ // fit the new stack bounds.
+ task.getBounds(mTmpRect);
+ task.setBounds(bounds, config);
+ task.scrollLocked(mTmpRect);
+ } else {
+ task.setBounds(bounds, config);
+ }
} else {
Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
}
@@ -143,11 +154,6 @@
bounds = mTmpRect;
mFullscreen = true;
} else {
- // ensure bounds are entirely within the display rect
- if (!bounds.intersect(mTmpRect)) {
- // Can't set bounds outside the containing display.. Sorry!
- return false;
- }
mFullscreen = mTmpRect.equals(bounds);
}
}
@@ -189,8 +195,6 @@
return false;
}
- /** Bounds of the stack with other system factors taken into consideration. */
- @Override
public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -205,6 +209,12 @@
mDisplayContent.getLogicalDisplayRect(out);
}
+ /** Bounds of the stack with other system factors taken into consideration. */
+ @Override
+ public void getDimBounds(Rect out) {
+ getBounds(out);
+ }
+
void updateDisplayInfo(Rect bounds) {
if (mDisplayContent != null) {
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -383,7 +393,7 @@
if (dockedStack != null) {
dockedStack.getRawBounds(mTmpRect2);
}
- final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
@@ -447,7 +457,7 @@
* close to the side of the dock.
* @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
- private static void getStackDockedModeBounds(
+ private void getStackDockedModeBounds(
Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
boolean dockOnTopOrLeft) {
final boolean dockedStack = stackId == DOCKED_STACK_ID;
@@ -455,6 +465,10 @@
outBounds.set(displayRect);
if (dockedStack) {
+ if (mService.mDockedStackCreateBounds != null) {
+ outBounds.set(mService.mDockedStackCreateBounds);
+ return;
+ }
// The initial bounds of the docked stack when it is created half the screen space and
// its bounds can be adjusted after that. The bounds of all other stacks are adjusted
// to occupy whatever screen space the docked stack isn't occupying.
@@ -501,7 +515,7 @@
final Rect bounds = new Rect();
mDisplayContent.getLogicalDisplayRect(bounds);
if (!fullscreen) {
- final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
@@ -511,10 +525,11 @@
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (StackId.isResizeableByDockedStack(otherStackId)) {
+ if (StackId.isResizeableByDockedStack(otherStackId)
+ && !otherStack.mBounds.equals(bounds)) {
mService.mH.sendMessage(
mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
- 1 /*allowResizeInDockedMode*/, bounds));
+ 1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds));
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index a33fb13..2f890be 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -19,7 +19,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.view.DisplayInfo;
-import android.view.InputDevice;
+import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
@@ -44,6 +44,10 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Rect mTmpRect = new Rect();
+ private final Region mNonResizeableRegion = new Region();
+ private boolean mTwoFingerScrolling;
+ private boolean mInGestureDetection;
+ private GestureDetector mGestureDetector;
private int mPointerIconShape = STYLE_NOT_SPECIFIED;
public TaskTapPointerEventListener(WindowManagerService service,
@@ -54,8 +58,18 @@
mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
}
+ // initialize the object, note this must be done outside WindowManagerService
+ // ctor, otherwise it may cause recursion as some code in GestureDetector ctor
+ // depends on WMS being already created.
+ void init() {
+ mGestureDetector = new GestureDetector(
+ mService.mContext, new TwoFingerScrollListener(), mService.mH);
+ }
+
@Override
public void onPointerEvent(MotionEvent motionEvent) {
+ doGestureDetection(motionEvent);
+
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
@@ -84,17 +98,20 @@
mPointerId = -1;
}
}
+ if (motionEvent.getPointerCount() != 2) {
+ stopTwoFingerScroll();
+ }
break;
}
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
- final WindowState window = mDisplayContent.findWindowForControlPoint(x, y);
- if (window == null) {
+ final Task task = mDisplayContent.findTaskForControlPoint(x, y);
+ if (task == null) {
break;
}
- window.getVisibleBounds(mTmpRect, false);
+ task.getDimBounds(mTmpRect);
if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
int iconShape = STYLE_DEFAULT;
if (x < mTmpRect.left) {
@@ -143,14 +160,72 @@
}
mPointerId = -1;
}
+ stopTwoFingerScroll();
break;
}
}
}
- void setTouchExcludeRegion(Region newRegion) {
+ private void doGestureDetection(MotionEvent motionEvent) {
+ if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
+ return;
+ }
+ final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
+ final int x = (int) motionEvent.getX();
+ final int y = (int) motionEvent.getY();
+ final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
+ if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
+ // If we receive the following actions, or the pointer goes out of the area
+ // we're interested in, stop detecting and cancel the current detection.
+ mInGestureDetection = isTouchInside
+ && action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_POINTER_UP
+ && action != MotionEvent.ACTION_CANCEL;
+ if (mInGestureDetection) {
+ mGestureDetector.onTouchEvent(motionEvent);
+ } else {
+ MotionEvent cancelEvent = motionEvent.copy();
+ cancelEvent.cancel();
+ mGestureDetector.onTouchEvent(cancelEvent);
+ stopTwoFingerScroll();
+ }
+ }
+ }
+
+ private void onTwoFingerScroll(MotionEvent e) {
+ final int x = (int)e.getX(0);
+ final int y = (int)e.getY(0);
+ if (!mTwoFingerScrolling) {
+ mTwoFingerScrolling = true;
+ mService.mH.obtainMessage(
+ H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
+ }
+ }
+
+ private void stopTwoFingerScroll() {
+ if (mTwoFingerScrolling) {
+ mTwoFingerScrolling = false;
+ mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
+ }
+ }
+
+ private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ if (e2.getPointerCount() == 2) {
+ onTwoFingerScroll(e2);
+ return true;
+ }
+ stopTwoFingerScroll();
+ return false;
+ }
+ }
+
+ void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
synchronized (this) {
mTouchExcludeRegion.set(newRegion);
+ mNonResizeableRegion.set(nonResizeableRegion);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 38bd71d..46fab2a 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -67,7 +67,7 @@
private final WindowSurfacePlacer mWindowPlacerLocked;
/** Is any window animating? */
- boolean mAnimating;
+ private boolean mAnimating;
/** Is any app window animating? */
boolean mAppWindowAnimating;
@@ -168,7 +168,8 @@
appAnimator.wasAnimating = appAnimator.animating;
if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
appAnimator.animating = true;
- mAnimating = mAppWindowAnimating = true;
+ setAnimating(true);
+ mAppWindowAnimating = true;
} else if (appAnimator.wasAnimating) {
// stopped animating, do one more pass through the layout
setAppLayoutChanges(appAnimator,
@@ -186,7 +187,8 @@
final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator;
appAnimator.wasAnimating = appAnimator.animating;
if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
- mAnimating = mAppWindowAnimating = true;
+ setAnimating(true);
+ mAppWindowAnimating = true;
} else if (appAnimator.wasAnimating) {
// stopped animating, do one more pass through the layout
setAppLayoutChanges(appAnimator,
@@ -282,7 +284,7 @@
final boolean wasAnimating = winAnimator.mWasAnimating;
final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
winAnimator.mWasAnimating = nowAnimating;
- mAnimating |= nowAnimating;
+ orAnimating(nowAnimating);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
@@ -546,7 +548,7 @@
}
}
}
- mAnimating = true;
+ setAnimating(true);
}
// If this window's app token is running a detached wallpaper
@@ -617,7 +619,7 @@
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
- mAnimating |= appAnimator.showAllWindowsLocked();
+ orAnimating(appAnimator.showAllWindowsLocked());
}
}
}
@@ -636,7 +638,7 @@
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
- mAnimating = false;
+ setAnimating(false);
mAppWindowAnimating = false;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
@@ -657,7 +659,7 @@
displayAnimator.mScreenRotationAnimation;
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
- mAnimating = true;
+ setAnimating(true);
} else {
mBulkUpdateParams |= SET_UPDATE_ROTATION;
screenRotationAnimation.kill();
@@ -697,7 +699,7 @@
screenRotationAnimation.updateSurfacesInTransaction();
}
- mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
+ orAnimating(mService.getDisplayContentLocked(displayId).animateDimLayers());
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null
@@ -706,6 +708,10 @@
}
}
+ if (mService.mDragState != null) {
+ mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
+ }
+
if (mAnimating) {
mService.scheduleAnimationLocked();
}
@@ -920,4 +926,16 @@
private class DisplayContentsAnimator {
ScreenRotationAnimation mScreenRotationAnimation = null;
}
+
+ boolean isAnimating() {
+ return mAnimating;
+ }
+
+ void setAnimating(boolean animating) {
+ mAnimating = animating;
+ }
+
+ void orAnimating(boolean animating) {
+ mAnimating |= animating;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6ad5b93..21d3bba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -50,10 +50,13 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
+import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
import android.Manifest;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -107,6 +110,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.TypedValue;
@@ -306,6 +310,8 @@
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -400,7 +406,7 @@
/**
* Windows whose surface should be destroyed.
*/
- final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
+ private final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
* Windows with a preserved surface waiting to be destroyed. These windows
@@ -437,11 +443,11 @@
WindowState[] mRebuildTmp = new WindowState[20];
/**
- * Stores for each user whether screencapture is disabled
+ * Stores for each user whether screencapture is disabled for all their windows.
* This array is essentially a cache for all userId for
* {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
*/
- SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<>();
+ private SparseBooleanArray mScreenCaptureDisabled = new SparseBooleanArray();
IInputMethodManager mInputMethodManager;
@@ -477,7 +483,8 @@
private boolean mKeyguardWaitingForActivityDrawn;
- static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ int mDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ Rect mDockedStackCreateBounds;
private final SparseIntArray mTmpTaskIds = new SparseIntArray();
@@ -750,9 +757,6 @@
private boolean completeDropLw(float x, float y) {
WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
- mDragState.mCurrentX = x;
- mDragState.mCurrentY = y;
-
DropPermissionHolder dropPermissionHolder = null;
if (dropTargetWin != null &&
(mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
@@ -910,7 +914,7 @@
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
- mAppTransition = new AppTransition(context, mH, mWindowMap, mWindowPlacerLocked);
+ mAppTransition = new AppTransition(context, this);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
mActivityManager = ActivityManagerNative.getDefault();
@@ -2105,25 +2109,11 @@
executeAppTransition();
}
- /**
- * Returns whether screen capture is disabled for all windows of a specific user.
- */
- boolean isScreenCaptureDisabledLocked(int userId) {
- Boolean disabled = mScreenCaptureDisabled.get(userId);
- if (disabled == null) {
- return false;
- }
- return disabled;
- }
-
boolean isSecureLocked(WindowState w) {
- if ((w.mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+ if ((w.mAttrs.flags & FLAG_SECURE) != 0) {
return true;
}
- if (isScreenCaptureDisabledLocked(UserHandle.getUserId(w.mOwnerUid))) {
- return true;
- }
- return false;
+ return mScreenCaptureDisabled.get(UserHandle.getUserId(w.mOwnerUid));
}
/**
@@ -2647,8 +2637,10 @@
Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility
+ " newVis=" + viewVisibility, stack);
}
- if (viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || !win.mAppToken.clientHidden)) {
+ final AppWindowToken appToken = win.mAppToken;
+ final boolean visible = viewVisibility == View.VISIBLE
+ && (appToken == null ? win.mPolicyVisibility : !appToken.clientHidden);
+ if (visible) {
result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges,
oldVisibility);
try {
@@ -2734,8 +2726,12 @@
mWallpaperControllerLocked.updateWallpaperOffset(
win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
- if (win.mAppToken != null) {
- win.mAppToken.updateReportedVisibilityLocked();
+ if (appToken != null) {
+ appToken.updateReportedVisibilityLocked();
+ }
+ if (winAnimator.mReportSurfaceResized) {
+ winAnimator.mReportSurfaceResized = false;
+ result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
}
outFrame.set(win.mCompatFrame);
outOverscanInsets.set(win.mOverscanInsets);
@@ -2856,7 +2852,11 @@
// notifying the client to render to with an offset from the surface's top-left.
if (win.isDragResizeChanged()) {
win.setDragResizing();
- if (win.mHasSurface) {
+ // We can only change top level windows to the full-screen surface when
+ // resizing (as we only have one full-screen surface). So there is no need
+ // to preserve and destroy windows which are attached to another, they
+ // will keep their surface and its size may change over time.
+ if (win.mHasSurface && win.mAttachedWindow == null) {
winAnimator.preserveSurfaceLocked();
result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
}
@@ -2941,7 +2941,7 @@
final Rect frame = new Rect(0, 0, width, height);
final Rect insets = new Rect();
Rect surfaceInsets = null;
- final boolean fullscreen = win != null && win.isFullscreen(width, height);
+ final boolean fullscreen = win != null && win.isFrameFullscreen(displayInfo);
final boolean freeform = win != null && win.inFreeformWorkspace();
final boolean docked = win != null && win.inDockedWorkspace();
if (win != null) {
@@ -3564,7 +3564,7 @@
}
}
- void setFocusTaskRegion() {
+ void setFocusTaskRegionLocked() {
if (mFocusedApp != null) {
final Task task = mFocusedApp.mTask;
final DisplayContent displayContent = task.getDisplayContent();
@@ -3599,7 +3599,7 @@
if (changed) {
mFocusedApp = newFocus;
mInputMonitor.setFocusedAppLw(newFocus);
- setFocusTaskRegion();
+ setFocusTaskRegionLocked();
}
if (moveFocusNow && changed) {
@@ -3689,22 +3689,26 @@
synchronized (mWindowMap) {
mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback,
onAnimationFinishedCallback, scaleUp);
- if (!scaleUp) {
- // This is used by freeform to recents windows transition. We need to synchronize
- // the animation with the appearance of the content of recents, so we will make
- // animation stay on the last frame a little longer.
- mTmpTaskIds.clear();
- for (int i = specs.length - 1; i >= 0; i--) {
- mTmpTaskIds.put(specs[i].taskId, 0);
- }
- for (final WindowState win : mWindowMap.values()) {
- final Task task = win.getTask();
- if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1) {
- final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mAppAnimator != null) {
- appToken.mAppAnimator.startProlongAnimation();
- }
- }
+ prolongAnimationsFromSpecs(specs, scaleUp);
+
+ }
+ }
+
+ void prolongAnimationsFromSpecs(@NonNull AppTransitionAnimationSpec[] specs, boolean scaleUp) {
+ // This is used by freeform <-> recents windows transition. We need to synchronize
+ // the animation with the appearance of the content of recents, so we will make
+ // animation stay on the first or last frame a little longer.
+ mTmpTaskIds.clear();
+ for (int i = specs.length - 1; i >= 0; i--) {
+ mTmpTaskIds.put(specs[i].taskId, 0);
+ }
+ for (final WindowState win : mWindowMap.values()) {
+ final Task task = win.getTask();
+ if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1) {
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null && appToken.mAppAnimator != null) {
+ appToken.mAppAnimator.startProlongAnimation(scaleUp ?
+ PROLONG_ANIMATION_AT_START : PROLONG_ANIMATION_AT_END);
}
}
}
@@ -4617,9 +4621,10 @@
return (stack != null && stack.isVisibleLocked());
}
- public void setDockedStackCreateMode(int mode) {
+ public void setDockedStackCreateState(int mode, Rect bounds) {
synchronized (mWindowMap) {
- sDockedStackCreateMode = mode;
+ mDockedStackCreateMode = mode;
+ mDockedStackCreateBounds = bounds;
}
}
@@ -4807,7 +4812,8 @@
}
}
- public void positionTaskInStack(int taskId, int stackId, int position) {
+ public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds,
+ Configuration config) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: positioning taskId=" + taskId
+ " in stackId=" + stackId + " at " + position);
@@ -4823,7 +4829,7 @@
"positionTaskInStack: could not find stackId=" + stackId);
return;
}
- task.positionTaskInStack(stack, position);
+ task.positionTaskInStack(stack, position, bounds, config);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
@@ -4851,6 +4857,21 @@
}
}
+ public void scrollTask(int taskId, Rect bounds) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("scrollTask: taskId " + taskId
+ + " not found.");
+ }
+
+ if (task.scrollLocked(bounds)) {
+ task.getDisplayContent().layoutNeeded = true;
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
@@ -5702,7 +5723,7 @@
@Override
public void run() {
Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
- true);
+ true, 1f);
try {
receiver.send(bm);
} catch (RemoteException e) {
@@ -5721,18 +5742,20 @@
* @param displayId the Display to take a screenshot of.
* @param width the width of the target bitmap
* @param height the height of the target bitmap
+ * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
*/
@Override
- public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height) {
+ public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height,
+ float frameScale) {
if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
"screenshotApplications()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
- return screenshotApplicationsInner(appToken, displayId, width, height, false);
+ return screenshotApplicationsInner(appToken, displayId, width, height, false, frameScale);
}
Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
- boolean includeFullDisplay) {
+ boolean includeFullDisplay, float frameScale) {
final DisplayContent displayContent;
synchronized(mWindowMap) {
displayContent = getDisplayContentLocked(displayId);
@@ -5849,7 +5872,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
- ws.getVisibleBounds(stackBounds, !BOUNDS_FOR_TOUCH);
+ ws.getVisibleBounds(stackBounds);
if (!frame.intersect(stackBounds)) {
// Set frame empty if there's no intersection.
frame.setEmpty();
@@ -5861,7 +5884,7 @@
screenshotReady = true;
}
- if (ws.isFullscreen(dw, dh) && ws.isOpaqueDrawn()){
+ if (ws.isObscuringFullscreen(displayInfo)){
break;
}
}
@@ -5912,10 +5935,10 @@
}
if (width < 0) {
- width = frame.width();
+ width = (int) (frame.width() * frameScale);
}
if (height < 0) {
- height = frame.height();
+ height = (int) (frame.height() * frameScale);
}
// Tell surface flinger what part of the image to crop. Take the top
@@ -6903,27 +6926,25 @@
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- synchronized(displayContent.mDisplaySizeLock) {
- displayInfo.rotation = mRotation;
- displayInfo.logicalWidth = dw;
- displayInfo.logicalHeight = dh;
- displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
- displayInfo.appWidth = appWidth;
- displayInfo.appHeight = appHeight;
- displayInfo.getLogicalMetrics(mRealDisplayMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
- displayInfo.getAppMetrics(mDisplayMetrics);
- if (displayContent.mDisplayScalingDisabled) {
- displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
- } else {
- displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
- }
-
- mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
- displayContent.getDisplayId(), displayInfo);
-
- displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
+ displayInfo.rotation = mRotation;
+ displayInfo.logicalWidth = dw;
+ displayInfo.logicalHeight = dh;
+ displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
+ displayInfo.appWidth = appWidth;
+ displayInfo.appHeight = appHeight;
+ displayInfo.getLogicalMetrics(mRealDisplayMetrics,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ displayInfo.getAppMetrics(mDisplayMetrics);
+ if (displayContent.mDisplayScalingDisabled) {
+ displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
+ } else {
+ displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
+
+ mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
+ displayContent.getDisplayId(), displayInfo);
+
+ displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
if (false) {
Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight);
}
@@ -7067,26 +7088,46 @@
return true;
}
- private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
- WindowState win = null;
+ private void startScrollingTask(DisplayContent displayContent, int startX, int startY) {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
+ "startScrollingTask: " + "{" + startX + ", " + startY + "}");
+
+ Task task = null;
synchronized (mWindowMap) {
- win = displayContent.findWindowForControlPoint(startX, startY);
- if (win == null || !startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ int taskId = displayContent.taskIdFromPoint(startX, startY);
+ if (taskId >= 0) {
+ task = mTaskIdToTask.get(taskId);
+ }
+ if (task == null || !task.isDockedInEffect() || !startPositioningLocked(
+ task.getTopVisibleAppMainWindow(), false /*resize*/, startX, startY)) {
return;
}
}
try {
- mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ mActivityManager.setFocusedTask(task.mTaskId);
+ } catch(RemoteException e) {}
+ }
+
+ private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
+ Task task = null;
+ synchronized (mWindowMap) {
+ task = displayContent.findTaskForControlPoint(startX, startY);
+ if (task == null || !startPositioningLocked(
+ task.getTopVisibleAppMainWindow(), true /*resize*/, startX, startY)) {
+ return;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(task.mTaskId);
} catch(RemoteException e) {}
}
private boolean startPositioningLocked(
WindowState win, boolean resize, float startX, float startY) {
- if (WindowManagerService.DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "startPositioningLocked: win=" + win +
- ", resize=" + resize + ", {" + startX + ", " + startY + "}");
- }
- if (win == null || win.getAppToken() == null || !win.inFreeformWorkspace()) {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG, "startPositioningLocked: "
+ + "win=" + win + ", resize=" + resize + ", {" + startX + ", " + startY + "}");
+
+ if (win == null || win.getAppToken() == null) {
Slog.w(TAG, "startPositioningLocked: Bad window " + win);
return false;
}
@@ -7120,7 +7161,7 @@
}
private void finishPositioning() {
- if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "finishPositioning");
}
synchronized (mWindowMap) {
@@ -7160,9 +7201,11 @@
SurfaceControl surface = new SurfaceControl(session, "drag surface",
width, height, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
surface.setLayerStack(display.getLayerStack());
+ float alpha = 1;
if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
- surface.setAlpha(.7071f);
+ alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
}
+ surface.setAlpha(alpha);
if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG "
+ surface + ": CREATE");
@@ -7172,6 +7215,7 @@
mDragState = new DragState(this, token, surface, flags, winBinder);
mDragState.mPid = callerPid;
mDragState.mUid = callerUid;
+ mDragState.mOriginalAlpha = alpha;
token = mDragState.mToken = new Binder();
// 5 second timeout for this window to actually begin the drag
@@ -7330,6 +7374,7 @@
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
displayContent.initializeDisplayBaseInfo();
+ displayContent.mTapDetector.init();
}
}
}
@@ -7395,6 +7440,8 @@
public static final int RESIZE_STACK = 43;
public static final int RESIZE_TASK = 44;
+ public static final int TWO_FINGER_SCROLL_START = 45;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -7681,7 +7728,7 @@
synchronized (mWindowMap) {
// Since we're holding both mWindowMap and mAnimator we don't need to
// hold mAnimator.mLayoutToAnim.
- if (mAnimator.mAnimating || mAnimationScheduled) {
+ if (mAnimator.isAnimating() || mAnimationScheduled) {
// If we are animating, don't do the gc now but
// delay a bit so we don't interrupt the animation.
sendEmptyMessageDelayed(H.FORCE_GC, 2000);
@@ -7861,6 +7908,11 @@
}
break;
+ case TWO_FINGER_SCROLL_START: {
+ startScrollingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
+
case TAP_DOWN_OUTSIDE_TASK: {
startResizingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
}
@@ -8054,10 +8106,8 @@
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
- synchronized(displayContent.mDisplaySizeLock) {
- size.x = displayContent.mInitialDisplayWidth;
- size.y = displayContent.mInitialDisplayHeight;
- }
+ size.x = displayContent.mInitialDisplayWidth;
+ size.y = displayContent.mInitialDisplayHeight;
}
}
}
@@ -8067,10 +8117,8 @@
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
- synchronized(displayContent.mDisplaySizeLock) {
- size.x = displayContent.mBaseDisplayWidth;
- size.y = displayContent.mBaseDisplayHeight;
- }
+ size.x = displayContent.mBaseDisplayWidth;
+ size.y = displayContent.mBaseDisplayHeight;
}
}
}
@@ -8139,13 +8187,9 @@
}
}
- private void setForcedDisplayScalingModeLocked(DisplayContent displayContent,
- int mode) {
+ private void setForcedDisplayScalingModeLocked(DisplayContent displayContent, int mode) {
Slog.i(TAG, "Using display scaling mode: " + (mode == 0 ? "auto" : "off"));
-
- synchronized(displayContent.mDisplaySizeLock) {
- displayContent.mDisplayScalingDisabled = (mode != 0);
- }
+ displayContent.mDisplayScalingDisabled = (mode != 0);
reconfigureDisplayLocked(displayContent);
}
@@ -8163,13 +8207,11 @@
try {
width = Integer.parseInt(sizeStr.substring(0, pos));
height = Integer.parseInt(sizeStr.substring(pos+1));
- synchronized(displayContent.mDisplaySizeLock) {
- if (displayContent.mBaseDisplayWidth != width
- || displayContent.mBaseDisplayHeight != height) {
- Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);
- displayContent.mBaseDisplayWidth = width;
- displayContent.mBaseDisplayHeight = height;
- }
+ if (displayContent.mBaseDisplayWidth != width
+ || displayContent.mBaseDisplayHeight != height) {
+ Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height);
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
}
} catch (NumberFormatException ex) {
}
@@ -8186,11 +8228,9 @@
int density;
try {
density = Integer.parseInt(densityStr);
- synchronized(displayContent.mDisplaySizeLock) {
- if (displayContent.mBaseDisplayDensity != density) {
- Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);
- displayContent.mBaseDisplayDensity = density;
- }
+ if (displayContent.mBaseDisplayDensity != density) {
+ Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density);
+ displayContent.mBaseDisplayDensity = density;
}
} catch (NumberFormatException ex) {
}
@@ -8200,21 +8240,16 @@
int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DISPLAY_SCALING_FORCE, 0);
if (mode != 0) {
- synchronized(displayContent.mDisplaySizeLock) {
- Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED");
- displayContent.mDisplayScalingDisabled = true;
- }
+ Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED");
+ displayContent.mDisplayScalingDisabled = true;
}
}
// displayContent must not be null
private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) {
Slog.i(TAG, "Using new display size: " + width + "x" + height);
-
- synchronized(displayContent.mDisplaySizeLock) {
- displayContent.mBaseDisplayWidth = width;
- displayContent.mBaseDisplayHeight = height;
- }
+ displayContent.mBaseDisplayWidth = width;
+ displayContent.mBaseDisplayHeight = height;
reconfigureDisplayLocked(displayContent);
}
@@ -8250,9 +8285,7 @@
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
- synchronized(displayContent.mDisplaySizeLock) {
- return displayContent.mInitialDisplayDensity;
- }
+ return displayContent.mInitialDisplayDensity;
}
}
return -1;
@@ -8263,9 +8296,7 @@
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
- synchronized(displayContent.mDisplaySizeLock) {
- return displayContent.mBaseDisplayDensity;
- }
+ return displayContent.mBaseDisplayDensity;
}
}
return -1;
@@ -8300,10 +8331,7 @@
// displayContent must not be null
private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) {
Slog.i(TAG, "Using new display density: " + density);
-
- synchronized(displayContent.mDisplaySizeLock) {
- displayContent.mBaseDisplayDensity = density;
- }
+ displayContent.mBaseDisplayDensity = density;
reconfigureDisplayLocked(displayContent);
}
@@ -8394,12 +8422,10 @@
private void setOverscanLocked(DisplayContent displayContent,
int left, int top, int right, int bottom) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- synchronized (displayContent.mDisplaySizeLock) {
- displayInfo.overscanLeft = left;
- displayInfo.overscanTop = top;
- displayInfo.overscanRight = right;
- displayInfo.overscanBottom = bottom;
- }
+ displayInfo.overscanLeft = left;
+ displayInfo.overscanTop = top;
+ displayInfo.overscanRight = right;
+ displayInfo.overscanBottom = bottom;
mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
right, bottom);
@@ -8578,8 +8604,8 @@
} else if (wtoken != null) {
winAnimator.mAnimLayer =
w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
- if (wtoken.mWillReplaceWindow && wtoken.mAnimateReplacingWindow &&
- wtoken.mReplacingWindow != w) {
+ if (wtoken.mWillReplaceWindow && wtoken.mReplacingWindow != w
+ && wtoken.mAnimateReplacingWindow) {
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to
// immediately receive the same treatment, e.g. to be above the dock divider.
@@ -9986,14 +10012,11 @@
DisplayInfo displayInfo = displayContent.getDisplayInfo();
final Rect rect = new Rect();
mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
- synchronized (displayContent.mDisplaySizeLock) {
- displayInfo.overscanLeft = rect.left;
- displayInfo.overscanTop = rect.top;
- displayInfo.overscanRight = rect.right;
- displayInfo.overscanBottom = rect.bottom;
- mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
- displayId, displayInfo);
- }
+ displayInfo.overscanLeft = rect.left;
+ displayInfo.overscanTop = rect.top;
+ displayInfo.overscanRight = rect.right;
+ displayInfo.overscanBottom = rect.bottom;
+ mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId, displayInfo);
configureDisplayPolicyLocked(displayContent);
// TODO: Create an input channel for each display with touch capability.
@@ -10119,7 +10142,6 @@
* Hint to a token that its activity will relaunch, which will trigger removal and addition of
* a window.
* @param token Application token for which the activity will be relaunched.
- * @param animate Whether to animate the addition of the new window.
*/
public void setReplacingWindow(IBinder token, boolean animate) {
synchronized (mWindowMap) {
@@ -10133,6 +10155,15 @@
appWindowToken.mWillReplaceWindow = true;
appWindowToken.mHasReplacedWindow = false;
appWindowToken.mAnimateReplacingWindow = animate;
+
+ if (animate) {
+ // Set-up dummy animation so we can start treating windows associated with this
+ // token like they are in transition before the new app window is ready for us to
+ // run the real transition animation.
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "setReplacingWindow() Setting dummy animation on: " + appWindowToken);
+ appWindowToken.mAppAnimator.setDummyAnimation();
+ }
}
}
@@ -10152,14 +10183,42 @@
}
}
- boolean isDockedStackResizingLocked() {
- return getDefaultDisplayContentLocked().getDockedDividerController().isResizing();
+ public void setTaskResizeable(int taskId, boolean resizeable) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.setResizeable(resizeable);
+ }
+ }
}
static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
+ void scheduleSurfaceDestroy(WindowState win) {
+ mDestroySurface.add(win);
+ }
+
+ boolean destroySurfacesLocked() {
+ boolean wallpaperDestroyed = false;
+ for (int i = mDestroySurface.size() - 1; i >= 0; i--) {
+ WindowState win = mDestroySurface.get(i);
+ win.mDestroying = false;
+ if (mInputMethodWindow == win) {
+ mInputMethodWindow = null;
+ }
+ if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
+ wallpaperDestroyed = true;
+ }
+ if (!win.shouldSaveSurface()) {
+ win.mWinAnimator.destroySurfaceLocked();
+ }
+ }
+ mDestroySurface.clear();
+ return wallpaperDestroyed;
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
@@ -10201,7 +10260,7 @@
}
@Override
- public void setMagnificationCallbacks(MagnificationCallbacks callbacks) {
+ public void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks) {
synchronized (mWindowMap) {
if (mAccessibilityController == null) {
mAccessibilityController = new AccessibilityController(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 080a2d1..a2ca170 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,11 +16,57 @@
package com.android.server.wm;
+import com.android.server.input.InputWindowHandle;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IApplicationToken;
+import android.view.IWindow;
+import android.view.IWindowFocusObserver;
+import android.view.IWindowId;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
@@ -36,8 +82,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
@@ -46,47 +90,6 @@
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.graphics.Point;
-import android.os.PowerManager;
-import android.os.RemoteCallbackList;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.WorkSource;
-import android.util.DisplayMetrics;
-import android.util.TimeUtils;
-import android.view.Display;
-import android.view.IWindowFocusObserver;
-import android.view.IWindowId;
-
-import com.android.server.input.InputWindowHandle;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Matrix;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
class WindowList extends ArrayList<WindowState> {
}
@@ -106,8 +109,6 @@
// to capture touch events in that area.
static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
- static final boolean BOUNDS_FOR_TOUCH = true;
-
static final int DRAG_RESIZE_MODE_FREEFORM = 0;
static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
@@ -410,6 +411,10 @@
final private Rect mTmpRect = new Rect();
+ // This window often remains added but hidden, so we want to destroy its surface when it's not
+ // visible.
+ private final boolean mDestroySurfaceWhenHidden;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -457,6 +462,7 @@
mSubLayer = 0;
mInputWindowHandle = null;
mWinAnimator = null;
+ mDestroySurfaceWhenHidden = false;
return;
}
mDeathRecipient = deathRecipient;
@@ -555,6 +561,7 @@
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this,
displayContent.getDisplayId());
+ mDestroySurfaceWhenHidden = mAttrs.type == TYPE_DOCK_DIVIDER;
}
void attach() {
@@ -729,8 +736,18 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
+ if (isVisibleLw()) {
+ // We don't adjust the dock divider frame for reasons other than performance. The
+ // real reason is that if it gets adjusted before it is shown for the first time,
+ // it would get size (0, 0). This causes a problem when we finally show the dock
+ // divider and try to draw to it. We do set the surface size at that moment to
+ // the correct size, but it's too late for the Surface Flinger to make it
+ // available for view rendering and as a result the renderer receives size 1, 1.
+ // This way we just keep the divider at the original size and Surface Flinger
+ // will return the correct value to the renderer.
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
+ }
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
@@ -962,17 +979,15 @@
/**
* Retrieves the visible bounds of the window.
* @param bounds The rect which gets the bounds.
- * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible
- * bounds will be returned.
*/
- void getVisibleBounds(Rect bounds, boolean forTouch) {
+ void getVisibleBounds(Rect bounds) {
boolean intersectWithStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
bounds.setEmpty();
mTmpRect.setEmpty();
if (intersectWithStackBounds) {
final TaskStack stack = getStack();
if (stack != null) {
- stack.getBounds(mTmpRect);
+ stack.getDimBounds(mTmpRect);
} else {
intersectWithStackBounds = false;
}
@@ -990,12 +1005,6 @@
}
return;
}
- if (forTouch && inFreeformWorkspace()) {
- final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- final int delta = WindowManagerService.dipToPixel(
- RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
- bounds.inset(-delta, -delta);
- }
}
public long getInputDispatchingTimeoutNanos() {
@@ -1247,9 +1256,20 @@
&& (mAttachedWindow == null || !mAttachedWindow.hasMoved());
}
- boolean isFullscreen(int screenWidth, int screenHeight) {
- return mFrame.left <= 0 && mFrame.top <= 0 &&
- mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+ boolean isObscuringFullscreen(final DisplayInfo displayInfo) {
+ Task task = getTask();
+ if (task != null && task.mStack != null && !task.mStack.isFullscreen()) {
+ return false;
+ }
+ if (!isOpaqueDrawn() || !isFrameFullscreen(displayInfo)) {
+ return false;
+ }
+ return true;
+ }
+
+ boolean isFrameFullscreen(final DisplayInfo displayInfo) {
+ return mFrame.left <= 0 && mFrame.top <= 0
+ && mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
}
boolean isConfigChanged() {
@@ -1299,6 +1319,10 @@
mHasSurface = hasSurface;
}
+ boolean shouldDestroySurfaceWhenAnimationFinishes() {
+ return mExiting || (mDestroySurfaceWhenHidden && !mPolicyVisibilityAfterAnim);
+ }
+
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
@@ -1406,33 +1430,41 @@
}
boolean inDockedWorkspace() {
- return mAppToken != null && mAppToken.mTask != null && mAppToken.mTask.inDockedWorkspace();
+ Task task = getTask();
+ return task != null && task.inDockedWorkspace();
}
- int getTouchableRegion(Region region, int flags, InputMonitor inputMonitor) {
- final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
+ boolean isDockedInEffect() {
+ Task task = getTask();
+ return task != null && task.isDockedInEffect();
+ }
+
+ int getTouchableRegion(Region region, int flags) {
+ final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
if (modal && mAppToken != null) {
// Limit the outer touch to the activity stack region.
- flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- if (!inFreeformWorkspace()) {
- // If this is a modal window we need to dismiss it if it's not full screen and the
- // touch happens outside of the frame that displays the content. This means we
- // need to intercept touches outside of that window. The dim layer user
- // associated with the window (task or stack) will give us the good bounds, as
- // they would be used to display the dim layer.
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null) {
- dimLayerUser.getBounds(mTmpRect);
- } else {
- getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
- }
+ flags |= FLAG_NOT_TOUCH_MODAL;
+ // If this is a modal window we need to dismiss it if it's not full screen and the
+ // touch happens outside of the frame that displays the content. This means we
+ // need to intercept touches outside of that window. The dim layer user
+ // associated with the window (task or stack) will give us the good bounds, as
+ // they would be used to display the dim layer.
+ final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+ if (dimLayerUser != null) {
+ dimLayerUser.getDimBounds(mTmpRect);
} else {
+ getVisibleBounds(mTmpRect);
+ }
+ if (inFreeformWorkspace()) {
// For freeform windows we the touch region to include the whole surface for the
// shadows.
- getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int delta = WindowManagerService.dipToPixel(
+ RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+ mTmpRect.inset(-delta, -delta);
}
region.set(mTmpRect);
+ cropRegionToStackBoundsIfNeeded(region);
} else {
// Not modal or full screen modal
getTouchableRegion(region);
@@ -1573,6 +1605,10 @@
// Already showing.
return false;
}
+ if (!mHasSurface) {
+ mDestroying = false;
+ mWinAnimator.createSurfaceLocked();
+ }
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
@@ -1608,8 +1644,7 @@
doAnimation = false;
}
}
- boolean current = doAnimation ? mPolicyVisibilityAfterAnim
- : mPolicyVisibility;
+ final boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility;
if (!current) {
// Already hiding.
return false;
@@ -1620,11 +1655,9 @@
doAnimation = false;
}
}
- if (doAnimation) {
- mPolicyVisibilityAfterAnim = false;
- } else {
+ mPolicyVisibilityAfterAnim = false;
+ if (!doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
- mPolicyVisibilityAfterAnim = false;
mPolicyVisibility = false;
// Window is no longer visible -- make sure if we were waiting
// for it to be displayed before enabling the display, that
@@ -1704,7 +1737,7 @@
Task task = getTask();
if (task == null || task.inHomeStack()
- || task.getTopAppWindowToken() != mAppToken) {
+ || task.getTopVisibleAppToken() != mAppToken) {
// Don't save surfaces for home stack apps. These usually resume and draw
// first frame very fast. Saving surfaces are mostly a waste of memory.
// Don't save if the window is not the topmost window.
@@ -1768,26 +1801,41 @@
frame.right - inset.right, frame.bottom - inset.bottom);
}
- public void getTouchableRegion(Region outRegion) {
+ void getTouchableRegion(Region outRegion) {
final Rect frame = mFrame;
switch (mTouchableInsets) {
default:
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ case TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+ case TOUCHABLE_INSETS_CONTENT:
applyInsets(outRegion, frame, mGivenContentInsets);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+ case TOUCHABLE_INSETS_VISIBLE:
applyInsets(outRegion, frame, mGivenVisibleInsets);
break;
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
+ case TOUCHABLE_INSETS_REGION: {
final Region givenTouchableRegion = mGivenTouchableRegion;
outRegion.set(givenTouchableRegion);
outRegion.translate(frame.left, frame.top);
break;
}
}
+ cropRegionToStackBoundsIfNeeded(outRegion);
+ }
+
+ void cropRegionToStackBoundsIfNeeded(Region region) {
+ if (mAppToken == null || !mAppToken.mCropWindowsToStack) {
+ return;
+ }
+
+ final TaskStack stack = getStack();
+ if (stack == null) {
+ return;
+ }
+
+ stack.getDimBounds(mTmpRect);
+ region.op(mTmpRect, Region.Op.INTERSECT);
}
WindowList getWindowList() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 10f737f..9726034 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
@@ -24,12 +26,12 @@
import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowState.*;
+import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -37,22 +39,18 @@
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Debug;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
-import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
@@ -61,7 +59,6 @@
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* Keep track of animations and surface operations for a single WindowState.
@@ -101,6 +98,11 @@
* we must tell them application to resize (and thus redraw itself).
*/
boolean mSurfaceResized;
+ /**
+ * Whether we should inform the client on next relayoutWindow that
+ * the surface has been resized since last time.
+ */
+ boolean mReportSurfaceResized;
WindowSurfaceController mSurfaceController;
private WindowSurfaceController mPendingDestroySurface;
@@ -121,16 +123,12 @@
Rect mLastClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
- // Used to save animation distances between the time they are calculated and when they are
- // used.
- int mAnimDw;
- int mAnimDh;
+ // Used to save animation distances between the time they are calculated and when they are used.
+ private int mAnimDx;
+ private int mAnimDy;
/** Is the next animation to be started a window move animation? */
- boolean mAnimateMove = false;
-
- /** Are we currently running a window move animation? */
- boolean mAnimatingMove = false;
+ private boolean mAnimateMove = false;
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -183,6 +181,8 @@
int mAttrType;
+ private final Rect mTmpSize = new Rect();
+
WindowStateAnimator(final WindowState win) {
final WindowManagerService service = win.mService;
@@ -193,8 +193,8 @@
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent != null) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- mAnimDw = displayInfo.appWidth;
- mAnimDh = displayInfo.appHeight;
+ mAnimDx = displayInfo.appWidth;
+ mAnimDy = displayInfo.appHeight;
} else {
Slog.w(TAG, "WindowStateAnimator ctor: Display has been removed");
// This is checked on return and dealt with.
@@ -294,19 +294,19 @@
TAG, "Starting animation in " + this +
" @ " + currentTime + ": ww=" + mWin.mFrame.width() +
" wh=" + mWin.mFrame.height() +
- " dw=" + mAnimDw + " dh=" + mAnimDh +
+ " dx=" + mAnimDx + " dy=" + mAnimDy +
" scale=" + mService.getWindowAnimationScaleLocked());
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (mAnimateMove) {
mAnimateMove = false;
mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
- mAnimDw, mAnimDh);
+ mAnimDx, mAnimDy);
} else {
mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
}
- mAnimDw = displayInfo.appWidth;
- mAnimDh = displayInfo.appHeight;
+ mAnimDx = displayInfo.appWidth;
+ mAnimDy = displayInfo.appHeight;
mAnimation.setStartTime(mAnimationStartTime != -1
? mAnimationStartTime
: currentTime);
@@ -363,7 +363,6 @@
mAnimating = false;
mKeyguardGoingAwayAnimation = false;
- mAnimatingMove = false;
mLocalAnimating = false;
if (mAnimation != null) {
mAnimation.cancel();
@@ -442,12 +441,12 @@
if (!isWindowAnimating()) {
//TODO (multidisplay): Accessibility is supported only for the default display.
if (mService.mAccessibilityController != null
- && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ && mWin.getDisplayId() == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
}
- if (!mWin.mExiting) {
+ if (!mWin.shouldDestroySurfaceWhenAnimationFinishes()) {
return;
}
@@ -455,12 +454,13 @@
return;
}
- if (WindowManagerService.localLOGV) Slog.v(
- TAG, "Exit animation finished in " + this
- + ": remove=" + mWin.mRemoveOnExit);
+ if (localLOGV) Slog.v(TAG, "Exit animation finished in " + this + ": remove="
+ + mWin.mRemoveOnExit);
if (mSurfaceController != null && mSurfaceController.hasSurface()) {
- mService.mDestroySurface.add(mWin);
- mWin.mDestroying = true;
+ mService.scheduleSurfaceDestroy(mWin);
+ if (mWin.mExiting) {
+ mWin.mDestroying = true;
+ }
hide("finishExit");
}
mWin.mExiting = false;
@@ -496,7 +496,7 @@
}
if (mDrawState == DRAW_PENDING) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
- Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
+ Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in "
+ mSurfaceController);
if (DEBUG_STARTING_WINDOW && startingWindow) {
Slog.v(TAG, "Draw state now committed in " + mWin);
@@ -528,7 +528,7 @@
mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
- if (mDestroyPreservedSurfaceUponRedraw && result) {
+ if (mDestroyPreservedSurfaceUponRedraw) {
mService.mDestroyPreservedSurface.add(mWin);
}
return result;
@@ -581,52 +581,16 @@
flags |= SurfaceControl.SECURE;
}
- float left = w.mFrame.left + w.mXOffset;
- float top = w.mFrame.top + w.mYOffset;
-
- int width;
- int height;
- if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
- // for a scaled surface, we always want the requested
- // size.
- width = w.mRequestedWidth;
- height = w.mRequestedHeight;
- } else {
- // When we're doing a drag-resizing, request a surface that's fullscreen size,
- // so that we don't need to reallocate during the process. This also prevents
- // buffer drops due to size mismatch.
- final DisplayInfo displayInfo = w.getDisplayInfo();
- if (displayInfo != null && w.isDragResizing()) {
- left = 0;
- top = 0;
- width = displayInfo.logicalWidth;
- height = displayInfo.logicalHeight;
- } else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
- }
- }
-
- // Something is wrong and SurfaceFlinger will not like this,
- // try to revert to sane values
- if (width <= 0) {
- width = 1;
- }
- if (height <= 0) {
- height = 1;
- }
-
- // Adjust for surface insets.
- width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
- height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
- left -= attrs.surfaceInsets.left;
- top -= attrs.surfaceInsets.top;
+ mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
+ calculateSurfaceBounds(w, attrs);
+ final int width = mTmpSize.width();
+ final int height = mTmpSize.height();
if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Creating surface in session "
+ mSession.mSurfaceSession + " window " + this
+ " w=" + width + " h=" + height
- + " x=" + left + " y=" + top
+ + " x=" + mTmpSize.left + " y=" + mTmpSize.top
+ " format=" + attrs.format + " flags=" + flags);
}
@@ -682,7 +646,7 @@
return null;
}
- if (WindowManagerService.localLOGV) {
+ if (localLOGV) {
Slog.v(TAG, "Got surface: " + mSurfaceController
+ ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+ ", animLayer=" + mAnimLayer);
@@ -692,23 +656,74 @@
Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
WindowManagerService.logSurface(w, "CREATE pos=("
+ w.mFrame.left + "," + w.mFrame.top + ") ("
- + w.mCompatFrame.width() + "x" + w.mCompatFrame.height()
- + "), layer=" + mAnimLayer + " HIDE", null);
+ + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", null);
}
// Start a new transaction and apply position & offset.
final int layerStack = w.getDisplayContent().getDisplay().getLayerStack();
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "POS " + left + ", " + top, null);
- mSurfaceController.setPositionAndLayer(left, top, layerStack, mAnimLayer);
+ "POS " + mTmpSize.left + ", " + mTmpSize.top, null);
+ mSurfaceController.setPositionAndLayer(mTmpSize.left, mTmpSize.top, layerStack,
+ mAnimLayer);
mLastHidden = true;
- if (WindowManagerService.localLOGV) Slog.v(
+ if (localLOGV) Slog.v(
TAG, "Created surface " + this);
}
return mSurfaceController;
}
+ private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) {
+ if ((attrs.flags & FLAG_SCALED) != 0) {
+ // For a scaled surface, we always want the requested size.
+ mTmpSize.right = mTmpSize.left + w.mRequestedWidth;
+ mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight;
+ } else {
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ if (w.isDragResizing()) {
+ if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
+ mTmpSize.left = 0;
+ mTmpSize.top = 0;
+ }
+ final DisplayInfo displayInfo = w.getDisplayInfo();
+ mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth;
+ mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight;
+ } else {
+ mTmpSize.right = mTmpSize.left + w.mCompatFrame.width();
+ mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height();
+ }
+ }
+
+ // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values.
+ if (mTmpSize.width() < 1) {
+ Slog.w(TAG, "Width of " + w + " is not positive " + mTmpSize.width());
+ mTmpSize.right = mTmpSize.left + 1;
+ }
+ if (mTmpSize.height() < 1) {
+ Slog.w(TAG, "Height of " + w + " is not positive " + mTmpSize.height());
+ mTmpSize.bottom = mTmpSize.top + 1;
+ }
+
+ final int displayId = w.getDisplayId();
+ float scale = 1.0f;
+ // Magnification is supported only for the default display.
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
+ final MagnificationSpec spec =
+ mService.mAccessibilityController.getMagnificationSpecForWindowLocked(w);
+ if (spec != null && !spec.isNop()) {
+ scale = spec.scale;
+ }
+ }
+
+ // Adjust for surface insets.
+ mTmpSize.left -= scale * attrs.surfaceInsets.left;
+ mTmpSize.top -= scale * attrs.surfaceInsets.top;
+ mTmpSize.right += scale * (attrs.surfaceInsets.left + attrs.surfaceInsets.right);
+ mTmpSize.bottom += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom);
+ }
+
void destroySurfaceLocked() {
final AppWindowToken wtoken = mWin.mAppToken;
if (wtoken != null) {
@@ -721,7 +736,12 @@
if (mSurfaceController != null) {
int i = mWin.mChildWindows.size();
- while (i > 0) {
+ // When destroying a surface we want to make sure child windows
+ // are hidden. If we are preserving the surface until redraw though
+ // we intend to swap it out with another surface for resizing. In this case
+ // the window always remains visible to the user and the child windows
+ // should likewise remain visable.
+ while (!mDestroyPreservedSurfaceUponRedraw && i > 0) {
i--;
WindowState c = mWin.mChildWindows.get(i);
c.mAttachedHidden = true;
@@ -753,7 +773,14 @@
mPendingDestroySurface = mSurfaceController;
}
} else {
- WindowManagerService.logSurface(mWin, "DESTROY", null);
+ if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+ RuntimeException e = null;
+ if (!WindowManagerService.HIDE_STACK_CRAWLS) {
+ e = new RuntimeException();
+ e.fillInStackTrace();
+ }
+ WindowManagerService.logSurface(mWin, "DESTROY", null);
+ }
destroySurface();
}
// Don't hide wallpaper if we're deferring the surface destroy
@@ -891,7 +918,7 @@
tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
MagnificationSpec spec = mService.mAccessibilityController
.getMagnificationSpecForWindowLocked(mWin);
if (spec != null && !spec.isNop()) {
@@ -947,7 +974,7 @@
//Slog.i(TAG, "Not applying alpha transform");
}
- if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV)
+ if ((DEBUG_SURFACE_TRACE || localLOGV)
&& (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
+ " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
@@ -968,13 +995,13 @@
return;
}
- if (WindowManagerService.localLOGV) Slog.v(
+ if (localLOGV) Slog.v(
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
MagnificationSpec spec = null;
//TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+ if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
}
if (spec != null) {
@@ -1069,7 +1096,7 @@
} else if (w.mDecorFrame.isEmpty()) {
// Windows without policy decor aren't cropped.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
- } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) {
+ } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.isAnimating()) {
// If we're animating, the wallpaper crop should only be updated at the end of the
// animation.
mTmpClipRect.set(w.mSystemDecorRect);
@@ -1080,7 +1107,7 @@
applyDecorRect(w.mDecorFrame);
}
- final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
+ final boolean fullscreen = w.isFrameFullscreen(displayInfo);
final boolean isFreeformResizing =
w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
final Rect clipRect = mTmpClipRect;
@@ -1109,12 +1136,8 @@
// The clip rect was generated assuming (0,0) as the window origin,
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
- // We don't want to clip to stack bounds windows that are currently doing entrance
- // animation for docked window, otherwise the animating window will be suddenly cut off.
- if (!(mAnimator.mAnimating && w.inDockedWorkspace())) {
- adjustCropToStackBounds(w, clipRect, isFreeformResizing);
- }
+ adjustCropToStackBounds(w, clipRect, isFreeformResizing);
w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1127,100 +1150,57 @@
private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
final AppWindowToken appToken = w.mAppToken;
final Task task = w.getTask();
- // We don't apply the the stack bounds to the window that is being replaced, because it was
- // living in a different stack. If we suddenly crop it to the new stack bounds, it might
- // get cut off. We don't want it to happen, so we let it ignore the stack bounds until it
- // gets removed. The window that will replace it will abide them.
- if (task != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
- TaskStack stack = task.mStack;
- stack.getBounds(mTmpStackBounds);
- // When we resize we use the big surface approach, which means we can't trust the
- // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
- // hardcoding it, we use surface coordinates.
- final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
- w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
- final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
- w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
- // We need to do some acrobatics with surface position, because their clip region is
- // relative to the inside of the surface, but the stack bounds aren't.
- clipRect.left = Math.max(0,
- Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
- clipRect.top = Math.max(0,
- Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
- clipRect.right = Math.max(0,
- Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
- clipRect.bottom = Math.max(0,
- Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+ if (task == null || !appToken.mCropWindowsToStack) {
+ return;
}
+
+ // We don't apply the stack bounds crop if:
+ // 1. The window is currently animating docked mode, otherwise the animating window will be
+ // suddenly cut off.
+ // 2. The window that is being replaced during animation, because it was living in a
+ // different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
+ // We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
+ // The window that will replace it will abide them.
+ if (isAnimating() && (appToken.mWillReplaceWindow || w.inDockedWorkspace())) {
+ return;
+ }
+
+ final TaskStack stack = task.mStack;
+ stack.getDimBounds(mTmpStackBounds);
+ // When we resize we use the big surface approach, which means we can't trust the
+ // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
+ // hardcoding it, we use surface coordinates.
+ final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
+ w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
+ final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
+ w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+ // We need to do some acrobatics with surface position, because their clip region is
+ // relative to the inside of the surface, but the stack bounds aren't.
+ clipRect.left = Math.max(0,
+ Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
+ clipRect.top = Math.max(0,
+ Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
+ clipRect.right = Math.max(0,
+ Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
+ clipRect.bottom = Math.max(0,
+ Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
}
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
- float left = w.mShownPosition.x;
- float top = w.mShownPosition.y;
+ mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
+ calculateSurfaceBounds(w, w.getAttrs());
- int width;
- int height;
- if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
- // for a scaled surface, we always want the requested
- // size.
- width = w.mRequestedWidth;
- height = w.mRequestedHeight;
- } else {
- // When we're doing a drag-resizing, request a surface that's fullscreen size,
- // so that we don't need to reallocate during the process. This also prevents
- // buffer drops due to size mismatch.
- final DisplayInfo displayInfo = w.getDisplayInfo();
-
- // In freeform resize mode, put surface at 0/0.
- if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
- left = 0;
- top = 0;
- }
- if (displayInfo != null && w.isDragResizing()) {
- width = displayInfo.logicalWidth;
- height = displayInfo.logicalHeight;
- } else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
- }
- }
-
- // Something is wrong and SurfaceFlinger will not like this,
- // try to revert to sane values
- if (width < 1) {
- width = 1;
- }
- if (height < 1) {
- height = 1;
- }
-
- // Adjust for surface insets.
- final LayoutParams attrs = w.getAttrs();
- final int displayId = w.getDisplayId();
- float scale = 1.0f;
- // Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
- MagnificationSpec spec =
- mService.mAccessibilityController.getMagnificationSpecForWindowLocked(w);
- if (spec != null && !spec.isNop()) {
- scale = spec.scale;
- }
- }
-
- width += scale * (attrs.surfaceInsets.left + attrs.surfaceInsets.right);
- height += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom);
- left -= scale * attrs.surfaceInsets.left;
- top -= scale * attrs.surfaceInsets.top;
-
- mSurfaceController.setPositionInTransaction(left, top, recoveringMemory);
- mSurfaceResized = mSurfaceController.setSizeInTransaction(width, height,
+ mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, recoveringMemory);
+ mSurfaceResized = mSurfaceController.setSizeInTransaction(
+ mTmpSize.width(), mTmpSize.height(),
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale,
recoveringMemory);
if (mSurfaceResized) {
+ mReportSurfaceResized = true;
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
w.applyDimLayerIfNeeded();
@@ -1449,7 +1429,7 @@
// Force the show in the next prepareSurfaceLocked() call.
mLastAlpha = -1;
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM)
- Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + this);
+ Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + mWin);
mDrawState = HAS_DRAWN;
mService.scheduleAnimationLocked();
@@ -1532,7 +1512,7 @@
applyAnimationLocked(transit, true);
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null
- && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ && mWin.getDisplayId() == DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
}
@@ -1633,7 +1613,7 @@
fadeOut.setDuration(fadeDuration);
fadeOut.setStartOffset(elapsed);
newAnimation.addAnimation(fadeOut);
- newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDw, mAnimDh);
+ newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDx, mAnimDy);
mAnimation = newAnimation;
}
@@ -1707,4 +1687,13 @@
mSurfaceController.destroyInTransaction();
mSurfaceController = null;
}
+
+ void setMoveAnimation(int left, int top) {
+ final Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ setAnimation(a);
+ mAnimDx = mWin.mLastFrame.left - left;
+ mAnimDy = mWin.mLastFrame.top - top;
+ mAnimateMove = true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f8b8d6c..b3c23d1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -296,10 +296,8 @@
}
boolean showRobustlyInTransaction() {
- if (SHOW_TRANSACTIONS) logSurface(
- "SHOW (performLayout)", null);
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
- + " during relayout");
+ if (SHOW_TRANSACTIONS) logSurface("SHOW (performLayout)", null);
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this + " during relayout");
if (mHiddenForCrop) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4b9a538..54d18e9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -17,7 +17,6 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
@@ -57,7 +56,6 @@
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -383,25 +381,7 @@
}
// Destroy the surface of any windows that are no longer visible.
- boolean wallpaperDestroyed = false;
- i = mService.mDestroySurface.size();
- if (i > 0) {
- do {
- i--;
- WindowState win = mService.mDestroySurface.get(i);
- win.mDestroying = false;
- if (mService.mInputMethodWindow == win) {
- mService.mInputMethodWindow = null;
- }
- if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
- wallpaperDestroyed = true;
- }
- if (!win.shouldSaveSurface()) {
- win.mWinAnimator.destroySurfaceLocked();
- }
- } while (i > 0);
- mService.mDestroySurface.clear();
- }
+ final boolean wallpaperDestroyed = mService.destroySurfacesLocked();
// Time to remove any exiting tokens?
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -534,7 +514,7 @@
if (updateInputWindowsNeeded) {
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.setFocusTaskRegion();
+ mService.setFocusTaskRegionLocked();
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -542,11 +522,8 @@
mService.scheduleAnimationLocked();
- if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG,
- "performSurfacePlacementInner exit: animating="
- + mService.mAnimator.mAnimating);
- }
+ if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
+ "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
}
private void applySurfaceChangesTransaction(boolean recoveringMemory, int numDisplays,
@@ -666,7 +643,7 @@
// Update effect.
w.mObscured = mObscured;
if (!mObscured) {
- handleNotObscuredLocked(w, innerDw, innerDh);
+ handleNotObscuredLocked(w, displayInfo);
}
w.applyDimLayerIfNeeded();
@@ -682,20 +659,17 @@
final WindowStateAnimator winAnimator = w.mWinAnimator;
// If the window has moved due to its containing content frame changing, then
- // notify the listeners and optionally animate it.
+ // notify the listeners and optionally animate it. Simply checking a change of
+ // position is not enough, because being move due to dock divider is not a trigger
+ // for animation.
if (w.hasMoved()) {
// Frame has moved, containing content frame has also moved, and we're not
// currently animating... let's do something.
final int left = w.mFrame.left;
final int top = w.mFrame.top;
- if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0) {
- Animation a = AnimationUtils.loadAnimation(mService.mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - left;
- winAnimator.mAnimDh = w.mLastFrame.top - top;
- winAnimator.mAnimateMove = true;
- winAnimator.mAnimatingMove = true;
+ if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+ && !w.isDragResizing()) {
+ winAnimator.setMoveAnimation(left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -932,6 +906,13 @@
win.prelayout();
mService.mPolicy.layoutWindowLw(win, null);
win.mLayoutSeq = seq;
+
+ // Window frames may have changed. Update dim layer with the new bounds.
+ final Task task = win.getTask();
+ if (task != null) {
+ displayContent.mDimLayerController.updateDimLayer(task);
+ }
+
if (DEBUG_LAYOUT) Slog.v(TAG,
" LAYOUT: mFrame="
+ win.mFrame + " mContainingFrame="
@@ -986,7 +967,7 @@
}
}
- // Window frames may have changed. Tell the input dispatcher about it.
+ // Window frames may have changed. Tell the input dispatcher about it.
mService.mInputMonitor.setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -1177,7 +1158,7 @@
">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
SurfaceControl.openTransaction();
try {
- mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
} finally {
SurfaceControl.closeTransaction();
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -1333,16 +1314,14 @@
/**
* @param w WindowState this method is applied to.
- * @param innerDw Width of app window.
- * @param innerDh Height of app window.
+ * @param dispInfo info of the display that the window's obscuring state is checked against.
*/
- private void handleNotObscuredLocked(final WindowState w, final int innerDw, final int innerDh) {
+ private void handleNotObscuredLocked(final WindowState w, final DisplayInfo dispInfo) {
final WindowManager.LayoutParams attrs = w.mAttrs;
final int attrFlags = attrs.flags;
final boolean canBeSeen = w.isDisplayedLw();
- final boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
- if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
+ if (canBeSeen && w.isObscuringFullscreen(dispInfo)) {
// This window completely covers everything behind it,
// so we want to leave all of them as undimmed (for
// performance reasons).
@@ -1460,7 +1439,7 @@
appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
}
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- mService.mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
}
}
}
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 64278ed..03fbd19 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -22,32 +22,69 @@
#include <utils/misc.h>
#include <utils/Log.h>
-#include <hardware_legacy/vibrator.h>
+#include <hardware/vibrator.h>
#include <stdio.h>
namespace android
{
+static hw_module_t *gVibraModule = NULL;
+static vibrator_device_t *gVibraDevice = NULL;
+
+static void vibratorInit(JNIEnv /* env */, jobject /* clazz */)
+{
+ if (gVibraModule != NULL) {
+ return;
+ }
+
+ int err = hw_get_module(VIBRATOR_HARDWARE_MODULE_ID, (hw_module_t const**)&gVibraModule);
+
+ if (err) {
+ ALOGE("Couldn't load %s module (%s)", VIBRATOR_HARDWARE_MODULE_ID, strerror(-err));
+ } else {
+ if (gVibraModule) {
+ vibrator_open(gVibraModule, &gVibraDevice);
+ }
+ }
+}
+
static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */)
{
- return vibrator_exists() > 0 ? JNI_TRUE : JNI_FALSE;
+ if (gVibraModule && gVibraDevice) {
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
}
static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
{
- // ALOGI("vibratorOn\n");
- vibrator_on(timeout_ms);
+ if (gVibraDevice) {
+ int err = gVibraDevice->vibrator_on(gVibraDevice, timeout_ms);
+ if (err != 0) {
+ ALOGE("The hw module failed in vibrator_on: %s", strerror(-err));
+ }
+ } else {
+ ALOGW("Tried to vibrate but there is no vibrator device.");
+ }
}
static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
{
- // ALOGI("vibratorOff\n");
- vibrator_off();
+ if (gVibraDevice) {
+ int err = gVibraDevice->vibrator_off(gVibraDevice);
+ if (err != 0) {
+ ALOGE("The hw module failed in vibrator_off(): %s", strerror(-err));
+ }
+ } else {
+ ALOGW("Tried to stop vibrating but there is no vibrator device.");
+ }
}
static const JNINativeMethod method_table[] = {
{ "vibratorExists", "()Z", (void*)vibratorExists },
+ { "vibratorInit", "()V", (void*)vibratorInit },
{ "vibratorOn", "(J)V", (void*)vibratorOn },
{ "vibratorOff", "()V", (void*)vibratorOff }
};
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index be190cb..b5654ee 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -152,18 +152,23 @@
getInputWindowHandleObjLocalRef(env);
}
-static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style,
- SpriteIcon* outSpriteIcon) {
- PointerIcon pointerIcon;
+static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
+ PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
status_t status = android_view_PointerIcon_loadSystemIcon(env,
- contextObj, style, &pointerIcon);
+ contextObj, style, outPointerIcon);
if (!status) {
- pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, kN32_SkColorType);
- outSpriteIcon->hotSpotX = pointerIcon.hotSpotX;
- outSpriteIcon->hotSpotY = pointerIcon.hotSpotY;
+ outPointerIcon->bitmap.copyTo(&outSpriteIcon->bitmap, kN32_SkColorType);
+ outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
+ outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
}
}
+static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style,
+ SpriteIcon* outSpriteIcon) {
+ PointerIcon pointerIcon;
+ loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
+}
+
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -238,7 +243,8 @@
/* --- PointerControllerPolicyInterface implementation --- */
virtual void loadPointerResources(PointerResources* outResources);
- virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources);
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources);
virtual int32_t getDefaultPointerIconId();
private:
@@ -1041,14 +1047,31 @@
&outResources->spotAnchor);
}
-void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) {
+void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources) {
JNIEnv* env = jniEnv();
for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING;
++iconId) {
- loadSystemIconAsSprite(env, mContextObj, iconId, &((*outResources)[iconId]));
+ PointerIcon pointerIcon;
+ loadSystemIconAsSpriteWithPointerIcon(
+ env, mContextObj, iconId, &pointerIcon, &((*outResources)[iconId]));
+ if (!pointerIcon.bitmapFrames.empty()) {
+ PointerAnimation& animationData = (*outAnimationResources)[iconId];
+ size_t numFrames = pointerIcon.bitmapFrames.size() + 1;
+ animationData.durationPerFrame =
+ milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
+ animationData.animationFrames.reserve(numFrames);
+ animationData.animationFrames.push_back(SpriteIcon(
+ pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ for (size_t i = 0; i < numFrames - 1; ++i) {
+ animationData.animationFrames.push_back(SpriteIcon(
+ pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ }
+ }
}
- loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL, &((*outResources)[POINTER_ICON_STYLE_NULL]));
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL,
+ &((*outResources)[POINTER_ICON_STYLE_NULL]));
}
int32_t NativeInputManager::getDefaultPointerIconId() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eee7d92..f80a611 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -60,6 +60,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -70,8 +71,11 @@
import android.net.ConnectivityManager;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -156,11 +160,6 @@
/**
* Implementation of the device policy APIs.
- *
- * Locking policies:
- * - {@link DevicePolicyManagerService} must not call into {@link IActivityManager} within {@code
- * this} lock to avoid lock inversion.
- * - Methods that call into {@link IActivityManager} must have the "AM" suffix.
*/
public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@@ -206,31 +205,6 @@
private static final int STATUS_BAR_DISABLE2_MASK =
StatusBarManager.DISABLE2_QUICK_SETTINGS;
- private static final Set<String> DEVICE_OWNER_USER_RESTRICTIONS;
- static {
- DEVICE_OWNER_USER_RESTRICTIONS = new ArraySet<>();
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_USB_FILE_TRANSFER);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_TETHERING);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_NETWORK_RESET);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FACTORY_RESET);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_ADD_USER);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SMS);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_FUN);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_SAFE_BOOT);
- DEVICE_OWNER_USER_RESTRICTIONS.add(UserManager.DISALLOW_CREATE_WINDOWS);
- }
-
- // The following user restrictions cannot be changed by any active admin, including device
- // owner and profile owner.
- private static final Set<String> IMMUTABLE_USER_RESTRICTIONS;
- static {
- IMMUTABLE_USER_RESTRICTIONS = new ArraySet<>();
- IMMUTABLE_USER_RESTRICTIONS.add(UserManager.DISALLOW_WALLPAPER);
- }
-
private static final Set<String> SECURE_SETTINGS_WHITELIST;
private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_WHITELIST;
private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
@@ -311,6 +285,11 @@
public void onBootPhase(int phase) {
mService.systemReady(phase);
}
+
+ @Override
+ public void onStartUser(int userHandle) {
+ mService.onStartUser(userHandle);
+ }
}
public static class DevicePolicyData {
@@ -441,7 +420,7 @@
"cross-profile-widget-providers";
private static final String TAG_PROVIDER = "provider";
private static final String TAG_PACKAGE_LIST_ITEM = "item";
-
+ private static final String TAG_KEEP_UNINSTALLED_PACKAGES = "keep-uninstalled-packages";
private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
final DeviceAdminInfo info;
@@ -514,6 +493,9 @@
// allowed.
List<String> permittedInputMethods;
+ // List of package names to keep cached.
+ List<String> keepUninstalledPackages;
+
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
@@ -699,6 +681,7 @@
writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
permittedAccessiblityServices);
writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
+ writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
if (hasUserRestrictions()) {
UserRestrictionsUtils.writeRestrictions(
out, userRestrictions, TAG_USER_RESTRICTIONS);
@@ -812,6 +795,8 @@
permittedAccessiblityServices = readPackageList(parser, tag);
} else if (TAG_PERMITTED_IMES.equals(tag)) {
permittedInputMethods = readPackageList(parser, tag);
+ } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
+ keepUninstalledPackages = readPackageList(parser, tag);
} else if (TAG_USER_RESTRICTIONS.equals(tag)) {
UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
} else {
@@ -1006,20 +991,23 @@
pw.println(disabledKeyguardFeatures);
pw.print(prefix); pw.print("crossProfileWidgetProviders=");
pw.println(crossProfileWidgetProviders);
- if (!(permittedAccessiblityServices == null)) {
+ if (permittedAccessiblityServices != null) {
pw.print(prefix); pw.print("permittedAccessibilityServices=");
- pw.println(permittedAccessiblityServices.toString());
+ pw.println(permittedAccessiblityServices);
}
- if (!(permittedInputMethods == null)) {
+ if (permittedInputMethods != null) {
pw.print(prefix); pw.print("permittedInputMethods=");
- pw.println(permittedInputMethods.toString());
+ pw.println(permittedInputMethods);
+ }
+ if (keepUninstalledPackages != null) {
+ pw.print(prefix); pw.print("keepUninstalledPackages=");
+ pw.println(keepUninstalledPackages);
}
pw.print(prefix); pw.println("userRestrictions:");
UserRestrictionsUtils.dumpRestrictions(pw, prefix + " ", userRestrictions);
}
}
- // DO NOT call it while taking the "this" lock, which could cause a dead lock.
private void handlePackagesChanged(String packageName, int userHandle) {
boolean removed = false;
if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
@@ -1065,11 +1053,8 @@
}
}
if (removed) {
- synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
- synchronized (DevicePolicyManagerService.this) {
- mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
- }
- }
+ // The removed admin might have disabled camera, so update user restrictions.
+ pushUserRestrictions(userHandle);
}
}
@@ -1097,6 +1082,10 @@
return LocalServices.getService(UserManagerInternal.class);
}
+ PackageManagerInternal getPackageManagerInternal() {
+ return LocalServices.getService(PackageManagerInternal.class);
+ }
+
NotificationManager getNotificationManager() {
return mContext.getSystemService(NotificationManager.class);
}
@@ -1110,7 +1099,7 @@
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
}
- IActivityManager getIActivityManagerInner() {
+ IActivityManager getIActivityManager() {
return ActivityManagerNative.getDefault();
}
@@ -1135,6 +1124,10 @@
return Looper.myLooper();
}
+ WifiManager getWifiManager() {
+ return mContext.getSystemService(WifiManager.class);
+ }
+
long binderClearCallingIdentity() {
return Binder.clearCallingIdentity();
}
@@ -1241,16 +1234,6 @@
}
/**
- * Caller must not hold {@code this} lock. See also the class javadoc.
- */
- final IActivityManager getIActivityManager() {
- if (Thread.holdsLock(this)) {
- Slog.wtfStack(LOG_TAG, "Call to ActivityManager detected within DPMS lock");
- }
- return mInjector.getIActivityManagerInner();
- }
-
- /**
* Instantiates the service.
*/
public DevicePolicyManagerService(Context context) {
@@ -1364,7 +1347,7 @@
// TODO PO may not have a class name either due to b/17652534. Address that too.
- // TODO Notify UM to update restrictions (?)
+ updateDeviceOwnerLocked();
}
}
@@ -1410,11 +1393,14 @@
}
migrated = true;
- // Migrate user 0 restrictions to DO, except for "system" restrictions.
+ // Migrate user 0 restrictions to DO.
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
migrateUserRestrictionsForUser(UserHandle.SYSTEM, deviceOwnerAdmin,
- /* exceptionList =*/ UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS);
+ /* exceptionList =*/ null);
+
+ // Push DO user restrictions to user manager.
+ pushUserRestrictions(UserHandle.USER_SYSTEM);
mOwners.setDeviceOwnerUserRestrictionsMigrated();
}
@@ -1423,7 +1409,6 @@
final Set<String> normalExceptionList = Sets.newArraySet(
UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_SMS);
- normalExceptionList.addAll(UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS);
final Set<String> managedExceptionList = new ArraySet<>(normalExceptionList.size() + 1);
managedExceptionList.addAll(normalExceptionList);
@@ -1445,6 +1430,13 @@
migrateUserRestrictionsForUser(ui.getUserHandle(), profileOwnerAdmin,
exceptionList);
+
+ // Note if a secondary user has no PO but has a DA that disables camera, we
+ // don't get here and won't push the camera user restriction to UserManager
+ // here. That's okay because we'll push user restrictions anyway when a user
+ // starts. But we still do it because we want to let user manager persist
+ // upon migration.
+ pushUserRestrictions(userId);
}
mOwners.setProfileOwnerUserRestrictionsMigrated(userId);
@@ -1460,15 +1452,15 @@
final Bundle origRestrictions = mUserManagerInternal.getBaseUserRestrictions(
user.getIdentifier());
- final Bundle newSystemRestrictions = new Bundle();
+ final Bundle newBaseRestrictions = new Bundle();
final Bundle newOwnerRestrictions = new Bundle();
for (String key : origRestrictions.keySet()) {
if (!origRestrictions.getBoolean(key)) {
continue;
}
- if (exceptionList.contains(key)) {
- newSystemRestrictions.putBoolean(key, true);
+ if (exceptionList!= null && exceptionList.contains(key)) {
+ newBaseRestrictions.putBoolean(key, true);
} else {
newOwnerRestrictions.putBoolean(key, true);
}
@@ -1476,11 +1468,11 @@
if (VERBOSE_LOG) {
Log.v(LOG_TAG, "origRestrictions=" + origRestrictions);
- Log.v(LOG_TAG, "newSystemRestrictions=" + newSystemRestrictions);
+ Log.v(LOG_TAG, "newBaseRestrictions=" + newBaseRestrictions);
Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions);
}
mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
- newSystemRestrictions);
+ newBaseRestrictions);
if (admin != null) {
admin.ensureUserRestrictions().clear();
@@ -1625,25 +1617,17 @@
@VisibleForTesting
boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
int userId) {
- boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
- boolean ownsProfile = (getProfileOwner(userId) != null
- && getProfileOwner(userId).getPackageName()
- .equals(admin.info.getPackageName()));
+ final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId);
+ final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId);
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || (ownsDevice && ownsProfile)) {
- return true;
- }
+ return ownsDevice;
} else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile) {
- return true;
- }
+ // DO always has the PO power.
+ return ownsDevice || ownsProfile;
} else {
- if (admin.info.usesPolicy(reqPolicy)) {
- return true;
- }
+ return admin.info.usesPolicy(reqPolicy);
}
- return false;
}
void sendAdminCommandLocked(ActiveAdmin admin, String action) {
@@ -1730,18 +1714,16 @@
updateMaximumTimeToLockLocked(policy);
policy.mRemovingAdmins.remove(adminReceiver);
}
- synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
- synchronized (DevicePolicyManagerService.this) {
- mUserManagerInternal.updateEffectiveUserRestrictionsLR(
- userHandle);
- }
- }
+ // The removed admin might have disabled camera, so update user
+ // restrictions.
+ pushUserRestrictions(userHandle);
}
});
}
}
- public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle) {
+ public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
+ boolean throwForMissiongPermission) {
if (!mHasFeature) {
return null;
}
@@ -1756,8 +1738,20 @@
throw new IllegalArgumentException("Unknown admin: " + adminName);
}
+ final ResolveInfo ri = infos.get(0);
+
+ if (!permission.BIND_DEVICE_ADMIN.equals(ri.activityInfo.permission)) {
+ final String message = "DeviceAdminReceiver " + adminName + " must be protected with"
+ + permission.BIND_DEVICE_ADMIN;
+ Slog.w(LOG_TAG, message);
+ if (throwForMissiongPermission &&
+ ri.activityInfo.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
try {
- return new DeviceAdminInfo(mContext, infos.get(0));
+ return new DeviceAdminInfo(mContext, ri);
} catch (XmlPullParserException e) {
Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
e);
@@ -1948,7 +1942,8 @@
String name = parser.getAttributeValue(null, "name");
try {
DeviceAdminInfo dai = findAdmin(
- ComponentName.unflattenFromString(name), userHandle);
+ ComponentName.unflattenFromString(name), userHandle,
+ /* throwForMissionPermission= */ false);
if (VERBOSE_LOG
&& (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
!= userHandle)) {
@@ -2050,23 +2045,39 @@
validatePasswordOwnerLocked(policy);
updateMaximumTimeToLockLocked(policy);
- updateLockTaskPackages(policy.mLockTaskPackages, userHandle);
+ updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
}
- private void updateLockTaskPackages(List<String> packages, final int userId) {
- final String[] copy = packages.toArray(new String[packages.size()]);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- getIActivityManager().updateLockTaskPackages(userId, copy);
- } catch (RemoteException willNotHappen) {
- }
+ private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ mInjector.getIActivityManager()
+ .updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateDeviceOwnerLocked() {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ // TODO This is to prevent DO from getting "clear data"ed, but it should also check the
+ // user id and also protect all other DAs too.
+ final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
+ if (deviceOwnerComponent != null) {
+ mInjector.getIActivityManager()
+ .updateDeviceOwner(deviceOwnerComponent.getPackageName());
}
- });
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
static void validateQualityConstant(int quality) {
@@ -2121,37 +2132,32 @@
getUserData(UserHandle.USER_SYSTEM);
loadOwners();
cleanUpOldUsers();
+
+ onStartUser(UserHandle.USER_SYSTEM);
+
// Register an observer for watching for user setup complete.
new SetupContentObserver(mHandler).register(mContext.getContentResolver());
// Initialize the user setup state, to handle the upgrade case.
updateUserSetupComplete();
- // Update the screen capture disabled cache in the window manager
- List<UserInfo> users = mUserManager.getUsers(true);
- final int N = users.size();
- for (int i = 0; i < N; i++) {
- int userHandle = users.get(i).id;
- updateScreenCaptureDisabledInWindowManager(userHandle,
- getScreenCaptureDisabled(null, userHandle));
+ List<String> packageList;
+ synchronized (this) {
+ packageList = getKeepUninstalledPackagesLocked();
+ }
+ if (packageList != null) {
+ mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList);
}
}
private void ensureDeviceOwnerUserStarted() {
- if (!mOwners.hasDeviceOwner()) {
- return;
- }
- final int userId = mOwners.getDeviceOwnerUserId();
- if (userId == UserHandle.USER_SYSTEM) {
- return;
- }
- if (VERBOSE_LOG) {
- Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ if (mOwners.hasDeviceOwner()) {
+ final int userId = mOwners.getDeviceOwnerUserId();
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "Starting non-system DO user: " + userId);
+ }
+ if (userId != UserHandle.USER_SYSTEM) {
try {
- getIActivityManager().startUserInBackground(userId);
+ mInjector.getIActivityManager().startUserInBackground(userId);
// STOPSHIP Prevent the DO user from being killed.
@@ -2159,7 +2165,13 @@
Slog.w(LOG_TAG, "Exception starting user", e);
}
}
- });
+ }
+ }
+
+ private void onStartUser(int userId) {
+ updateScreenCaptureDisabledInWindowManager(userId,
+ getScreenCaptureDisabled(null, userId));
+ pushUserRestrictions(userId);
}
private void cleanUpOldUsers() {
@@ -2260,6 +2272,7 @@
// Build and show a warning notification
int smallIconId;
String contentText;
+ // TODO Why does it use the DO name? The cert APIs are all for PO. b/25772443
final String ownerName = getDeviceOwnerName();
if (isManagedProfile(userHandle.getIdentifier())) {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_administrator);
@@ -2321,7 +2334,8 @@
enforceCrossUserPermission(userHandle);
DevicePolicyData policy = getUserData(userHandle);
- DeviceAdminInfo info = findAdmin(adminReceiver, userHandle);
+ DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
+ /* throwForMissionPermission= */ true);
if (info == null) {
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
}
@@ -2425,19 +2439,15 @@
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
- return packageHasActiveAdminsLocked(packageName, userHandle);
- }
- }
-
- boolean packageHasActiveAdminsLocked(String packageName, int userHandle) {
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- if (policy.mAdminList.get(i).info.getPackageName().equals(packageName)) {
- return true;
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ if (policy.mAdminList.get(i).info.getPackageName().equals(packageName)) {
+ return true;
+ }
}
+ return false;
}
- return false;
}
@Override
@@ -2452,8 +2462,11 @@
return;
}
if (admin.getUid() != mInjector.binderGetCallingUid()) {
- // Active device owners must remain active admins.
- if (isDeviceOwner(adminReceiver)) {
+ // Active device/profile owners must remain active admins.
+ if (isDeviceOwner(adminReceiver, userHandle)
+ || isProfileOwner(adminReceiver, userHandle)) {
+ Slog.e(LOG_TAG, "Device/profile owner cannot be removed: component=" +
+ adminReceiver);
return;
}
mContext.enforceCallingOrSelfPermission(
@@ -3198,21 +3211,64 @@
}
@Override
- public boolean resetPassword(String passwordOrNull, int flags) {
+ public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
if (!mHasFeature) {
return false;
}
- final int userHandle = UserHandle.getCallingUserId();
- enforceNotManagedProfile(userHandle, "reset the password");
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int userHandle = mInjector.userHandleGetCallingUserId();
+
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ if (mUserManager.getCredentialOwnerProfile(userHandle) != userHandle) {
+ throw new SecurityException("You can not change password for this profile because"
+ + " it shares the password with the owner profile");
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
String password = passwordOrNull != null ? passwordOrNull : "";
int quality;
synchronized (this) {
- // This api can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
+ // If caller has PO (or DO), it can clear the password, so see if that's the case
+ // first.
+ ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
+ null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
+ if (admin == null) {
+ // Otherwise, make sure the caller has any active admin with the right policy.
+ admin = getActiveAdminForCallerLocked(null,
+ DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
+ }
+
+ final ComponentName adminComponent = admin.info.getComponent();
+
+ // As of N, only profile owners and device owners can reset the password.
+ if (!(isProfileOwner(adminComponent, userHandle)
+ || isDeviceOwner(adminComponent, userHandle))) {
+ final boolean preN = getTargetSdk(admin.info.getPackageName(), userHandle)
+ < android.os.Build.VERSION_CODES.N;
+ // As of N, password resetting to empty/null is not allowed anymore.
+ // TODO Should we allow DO/PO to set an empty password?
+ if (TextUtils.isEmpty(password)) {
+ if (!preN) {
+ throw new SecurityException("Cannot call with null password");
+ } else {
+ Slog.e(LOG_TAG, "Cannot call with null password");
+ return false;
+ }
+ }
+ // As of N, password cannot be changed by the admin if it is already set.
+ if (isLockScreenSecureUnchecked(userHandle)) {
+ if (!preN) {
+ throw new SecurityException("Admin cannot change current password");
+ } else {
+ Slog.e(LOG_TAG, "Admin cannot change current password");
+ return false;
+ }
+ }
+ }
quality = getPasswordQuality(null, userHandle);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int realQuality = LockPatternUtils.computePasswordQuality(password);
@@ -3298,7 +3354,6 @@
}
}
- int callingUid = mInjector.binderGetCallingUid();
DevicePolicyData policy = getUserData(userHandle);
if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
@@ -3314,9 +3369,9 @@
// Don't do this with the lock held, because it is going to call
// back in to the service.
- long ident = mInjector.binderClearCallingIdentity();
+ ident = mInjector.binderClearCallingIdentity();
try {
- LockPatternUtils utils = new LockPatternUtils(mContext);
+ LockPatternUtils utils = mInjector.newLockPatternUtils();
if (!TextUtils.isEmpty(password)) {
utils.saveLockPassword(password, null, quality, userHandle);
} else {
@@ -3341,6 +3396,15 @@
return true;
}
+ private boolean isLockScreenSecureUnchecked(int userId) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ return mInjector.newLockPatternUtils().isSecure(userId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
private void setDoNotAskCredentialsOnBoot() {
synchronized (this) {
DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
@@ -3696,10 +3760,11 @@
}
@Override
- public void wipeData(int flags, final int userHandle) {
+ public void wipeData(int flags) {
if (!mHasFeature) {
return;
}
+ final int userHandle = mInjector.userHandleGetCallingUserId();
enforceCrossUserPermission(userHandle);
synchronized (this) {
// This API can only be called by an active device admin,
@@ -3712,8 +3777,7 @@
long ident = mInjector.binderClearCallingIdentity();
try {
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- if (userHandle != UserHandle.USER_SYSTEM
- || !isDeviceOwner(admin.info.getComponent())) {
+ if (!isDeviceOwner(admin.info.getComponent(), userHandle)) {
throw new SecurityException(
"Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
}
@@ -3740,7 +3804,7 @@
@Override
public void run() {
try {
- IActivityManager am = getIActivityManager();
+ IActivityManager am = mInjector.getIActivityManager();
if (am.getCurrentUser().id == userHandle) {
am.switchUser(UserHandle.USER_SYSTEM);
}
@@ -3817,7 +3881,7 @@
}
enforceCrossUserPermission(userHandle);
// Managed Profile password can only be changed when per user encryption is present.
- if (!mContext.getSystemService(StorageManager.class).isPerUserEncryptionEnabled()) {
+ if (!StorageManager.isFileBasedEncryptionEnabled()) {
enforceNotManagedProfile(userHandle, "set the active password");
}
@@ -4272,15 +4336,18 @@
}
}
- private void updateScreenCaptureDisabledInWindowManager(int userHandle, boolean disabled) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
+ private void updateScreenCaptureDisabledInWindowManager(final int userHandle,
+ final boolean disabled) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mInjector.getIWindowManager().setScreenCaptureDisabled(userHandle, disabled);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+ }
+ }
+ });
}
/**
@@ -4336,7 +4403,7 @@
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
- final int userHandle = UserHandle.getCallingUserId();
+ final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
@@ -4346,15 +4413,7 @@
}
}
// Tell the user manager that the restrictions have changed.
- synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
- synchronized (this) {
- if (isDeviceOwner(who)) {
- mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersLR();
- } else {
- mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
- }
- }
- }
+ pushUserRestrictions(userHandle);
}
/**
@@ -4363,6 +4422,11 @@
*/
@Override
public boolean getCameraDisabled(ComponentName who, int userHandle) {
+ return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true);
+ }
+
+ private boolean getCameraDisabled(ComponentName who, int userHandle,
+ boolean mergeDeviceOwnerRestriction) {
if (!mHasFeature) {
return false;
}
@@ -4372,9 +4436,11 @@
return (admin != null) ? admin.disableCamera : false;
}
// First, see if DO has set it. If so, it's device-wide.
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner != null && deviceOwner.disableCamera) {
- return true;
+ if (mergeDeviceOwnerRestriction) {
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null && deviceOwner.disableCamera) {
+ return true;
+ }
}
// Then check each device admin on the user.
@@ -4472,6 +4538,42 @@
}
@Override
+ public void setKeepUninstalledPackages(ComponentName who, List<String> packageList) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ Preconditions.checkNotNull(packageList, "packageList is null");
+ final int userHandle = UserHandle.getCallingUserId();
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ admin.keepUninstalledPackages = packageList;
+ saveSettingsLocked(userHandle);
+ mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList);
+ }
+ }
+
+ @Override
+ public List<String> getKeepUninstalledPackages(ComponentName who) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ if (!mHasFeature) {
+ return null;
+ }
+ // TODO In split system user mode, allow apps on user 0 to query the list
+ synchronized (this) {
+ // Check if this is the device owner who is calling
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ return getKeepUninstalledPackagesLocked();
+ }
+ }
+
+ private List<String> getKeepUninstalledPackagesLocked() {
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) ? deviceOwner.keepUninstalledPackages : null;
+ }
+
+ @Override
public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (!mHasFeature) {
return false;
@@ -4496,6 +4598,7 @@
mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
+ updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
ident = mInjector.binderClearCallingIdentity();
@@ -4509,43 +4612,60 @@
}
}
- public boolean isDeviceOwner(ComponentName who) {
- if (!mHasFeature) {
- return false;
- }
- synchronized (this) {
- return mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerComponent().equals(who);
- }
- }
-
- @Override
- public boolean isDeviceOwnerPackage(String packageName) {
- if (!mHasFeature) {
- return false;
- }
+ public boolean isDeviceOwner(ComponentName who, int userId) {
synchronized (this) {
return mOwners.hasDeviceOwner()
- && mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName);
+ && mOwners.getDeviceOwnerUserId() == userId
+ && mOwners.getDeviceOwnerComponent().equals(who);
}
}
+ public boolean isProfileOwner(ComponentName who, int userId) {
+ final ComponentName profileOwner = getProfileOwner(userId);
+ return who != null && who.equals(profileOwner);
+ }
+
@Override
- public ComponentName getDeviceOwner() {
+ public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
if (!mHasFeature) {
return null;
}
+ if (!callingUserOnly) {
+ enforceManageUsers();
+ }
synchronized (this) {
+ if (!mOwners.hasDeviceOwner()) {
+ return null;
+ }
+ if (callingUserOnly && mInjector.userHandleGetCallingUserId() !=
+ mOwners.getDeviceOwnerUserId()) {
+ return null;
+ }
return mOwners.getDeviceOwnerComponent();
}
}
@Override
+ public int getDeviceOwnerUserId() {
+ if (!mHasFeature) {
+ return UserHandle.USER_NULL;
+ }
+ enforceManageUsers();
+ synchronized (this) {
+ return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
+ }
+ }
+
+ /**
+ * Returns the "name" of the device owner. It'll work for non-DO users too, but requires
+ * MANAGE_USERS.
+ */
+ @Override
public String getDeviceOwnerName() {
if (!mHasFeature) {
return null;
}
- // TODO: Do we really need it? getDeviceOwner() doesn't require it.
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceManageUsers();
synchronized (this) {
if (!mOwners.hasDeviceOwner()) {
return null;
@@ -4560,7 +4680,7 @@
// Returns the active device owner or null if there is no device owner.
@VisibleForTesting
ActiveAdmin getDeviceOwnerAdminLocked() {
- ComponentName component = getDeviceOwner();
+ ComponentName component = mOwners.getDeviceOwnerComponent();
if (component == null) {
return null;
}
@@ -4573,6 +4693,7 @@
return admin;
}
}
+ Slog.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component);
return null;
}
@@ -4588,15 +4709,25 @@
} catch (NameNotFoundException e) {
throw new SecurityException(e);
}
- if (!mOwners.hasDeviceOwner() || !getDeviceOwner().getPackageName().equals(packageName)
- || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
- throw new SecurityException("clearDeviceOwner can only be called by the device owner");
- }
synchronized (this) {
+ if (!mOwners.hasDeviceOwner()
+ || !mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName)
+ || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
+ throw new SecurityException(
+ "clearDeviceOwner can only be called by the device owner");
+ }
+
+ final ActiveAdmin admin = getDeviceOwnerAdminLocked();
+ if (admin != null) {
+ admin.disableCamera = false;
+ admin.userRestrictions = null;
+ }
+
clearUserPoliciesLocked(new UserHandle(UserHandle.USER_SYSTEM));
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
+ updateDeviceOwnerLocked();
// Reactivate backup service.
long ident = mInjector.binderClearCallingIdentity();
try {
@@ -4637,6 +4768,7 @@
final ActiveAdmin admin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
synchronized (this) {
+ admin.disableCamera = false;
admin.userRestrictions = null;
clearUserPoliciesLocked(callingUser);
final int userId = callingUser.getIdentifier();
@@ -4645,6 +4777,30 @@
}
}
+ @Override
+ public boolean setDeviceOwnerLockScreenInfo(ComponentName who, String info) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ if (!mHasFeature) {
+ return false;
+ }
+
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ long token = mInjector.binderClearCallingIdentity();
+ try {
+ new LockPatternUtils(mContext).setDeviceOwnerInfo(info);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public String getDeviceOwnerLockScreenInfo() {
+ return new LockPatternUtils(mContext).getDeviceOwnerInfo();
+ }
+
private void clearUserPoliciesLocked(UserHandle userHandle) {
int userId = userHandle.getIdentifier();
// Reset some of the user-specific policies
@@ -4659,9 +4815,7 @@
mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userHandle.getIdentifier());
- synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
- mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle.getIdentifier());
- }
+ pushUserRestrictions(userHandle.getIdentifier());
} catch (RemoteException re) {
} finally {
mInjector.binderRestoreCallingIdentity(ident);
@@ -4757,7 +4911,7 @@
if (!mHasFeature) {
return null;
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceManageUsers();
ComponentName profileOwner = getProfileOwner(userHandle);
if (profileOwner == null) {
return null;
@@ -4887,13 +5041,20 @@
}
}
+ private void enforceManageUsers() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ }
+ }
+
private void enforceCrossUserPermission(int userHandle) {
if (userHandle < 0) {
throw new IllegalArgumentException("Invalid userId " + userHandle);
}
final int callingUid = mInjector.binderGetCallingUid();
if (userHandle == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
+ " INTERACT_ACROSS_USERS_FULL permission");
@@ -4950,31 +5111,30 @@
return;
}
- final Printer p = new PrintWriterPrinter(pw);
-
synchronized (this) {
- p.println("Current Device Policy Manager state:");
+ pw.println("Current Device Policy Manager state:");
mOwners.dump(" ", pw);
int userCount = mUserData.size();
for (int u = 0; u < userCount; u++) {
DevicePolicyData policy = getUserData(mUserData.keyAt(u));
- p.println(" Enabled Device Admins (User " + policy.mUserHandle + "):");
+ pw.println();
+ pw.println(" Enabled Device Admins (User " + policy.mUserHandle + "):");
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin ap = policy.mAdminList.get(i);
if (ap != null) {
- pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString());
+ pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString());
pw.println(":");
- ap.dump(" ", pw);
+ ap.dump(" ", pw);
}
}
if (!policy.mRemovingAdmins.isEmpty()) {
- p.println(" Removing Device Admins (User " + policy.mUserHandle + "): "
+ pw.println(" Removing Device Admins (User " + policy.mUserHandle + "): "
+ policy.mRemovingAdmins);
}
pw.println(" ");
- pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner);
+ pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner);
}
}
}
@@ -5357,14 +5517,14 @@
}
}
- private boolean checkCallerIsCurrentUserOrProfileAM() {
+ private boolean checkCallerIsCurrentUserOrProfile() {
int callingUserId = UserHandle.getCallingUserId();
long token = mInjector.binderClearCallingIdentity();
try {
UserInfo currentUser;
UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
try {
- currentUser = getIActivityManager().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
return false;
@@ -5395,7 +5555,7 @@
// TODO When InputMethodManager supports per user calls remove
// this restriction.
- if (!checkCallerIsCurrentUserOrProfileAM()) {
+ if (!checkCallerIsCurrentUserOrProfile()) {
return false;
}
@@ -5447,7 +5607,7 @@
public List getPermittedInputMethodsForCurrentUser() {
UserInfo currentUser;
try {
- currentUser = getIActivityManager().getCurrentUser();
+ currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
// Activity managed is dead, just allow all IMEs
@@ -5542,7 +5702,7 @@
}
// Start user in background.
- getIActivityManager().startUserInBackground(userHandle);
+ mInjector.getIActivityManager().startUserInBackground(userHandle);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
}
@@ -5575,20 +5735,20 @@
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- long id = mInjector.binderClearCallingIdentity();
- try {
- int userId = UserHandle.USER_SYSTEM;
- if (userHandle != null) {
- userId = userHandle.getIdentifier();
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ int userId = UserHandle.USER_SYSTEM;
+ if (userHandle != null) {
+ userId = userHandle.getIdentifier();
+ }
+ return mInjector.getIActivityManager().switchUser(userId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Couldn't switch user", e);
+ return false;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
}
- return getIActivityManager().switchUser(userId);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Couldn't switch user", e);
- return false;
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5612,57 +5772,83 @@
}
}
- // DO NOT call it while taking the "this" lock, which could cause a dead lock.
@Override
public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
Preconditions.checkNotNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
- synchronized (mUserManagerInternal.getUserRestrictionsLock()) {
- synchronized (this) {
- ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- final boolean isDeviceOwner = isDeviceOwner(who);
- if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
- && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
- throw new SecurityException(
- "Profile owners cannot set user restriction " + key);
+ synchronized (this) {
+ ActiveAdmin activeAdmin =
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final boolean isDeviceOwner = isDeviceOwner(who, userHandle);
+ if (isDeviceOwner) {
+ if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
+ throw new SecurityException("Device owner cannot set user restriction " + key);
}
- if (IMMUTABLE_USER_RESTRICTIONS.contains(key)) {
- throw new SecurityException("User restriction " + key + " cannot be changed");
+ } else { // profile owner
+ if (!UserRestrictionsUtils.canProfileOwnerChange(key)) {
+ throw new SecurityException("Profile owner cannot set user restriction " + key);
}
-
- final long id = mInjector.binderClearCallingIdentity();
- try {
- // Save the restriction to ActiveAdmin.
- // TODO When DO sets a restriction, it'll always be treated as device-wide.
- // If there'll be a policy that can be set by both, we'll need scoping support,
- // and need to have another Bundle in DO active admin to hold restrictions as
- // PO.
- activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
- saveSettingsLocked(userHandle);
-
- // Tell UserManager the new value.
- if (isDeviceOwner) {
- mUserManagerInternal.updateEffectiveUserRestrictionsForAllUsersLR();
- } else {
- mUserManagerInternal.updateEffectiveUserRestrictionsLR(userHandle);
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
-
- sendChangedNotification(userHandle);
}
+
+ // Save the restriction to ActiveAdmin.
+ activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
+ saveSettingsLocked(userHandle);
+
+ pushUserRestrictions(userHandle);
+
+ sendChangedNotification(userHandle);
+ }
+ }
+
+ private void pushUserRestrictions(int userId) {
+ synchronized (this) {
+ final Bundle global;
+ final Bundle local = new Bundle();
+ if (mOwners.isDeviceOwnerUserId(userId)) {
+ global = new Bundle();
+
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null) {
+ return; // Shouldn't happen.
+ }
+
+ UserRestrictionsUtils.sortToGlobalAndLocal(deviceOwner.userRestrictions,
+ global, local);
+ // DO can disable camera globally.
+ if (deviceOwner.disableCamera) {
+ global.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ }
+ } else {
+ global = null;
+
+ ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+ if (profileOwner != null) {
+ UserRestrictionsUtils.merge(local, profileOwner.userRestrictions);
+ }
+ }
+ // Also merge in *local* camera restriction.
+ if (getCameraDisabled(/* who= */ null,
+ userId, /* mergeDeviceOwnerRestriction= */ false)) {
+ local.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ }
+ mUserManagerInternal.setDevicePolicyUserRestrictions(userId, local, global);
}
}
@Override
- public Bundle getUserRestrictions(ComponentName who) {
+ public Bundle getUserRestrictions(ComponentName who, int userHandle) {
Preconditions.checkNotNull(who, "ComponentName is null");
+ enforceCrossUserPermission(userHandle);
synchronized (this) {
- final ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle);
+ if (activeAdmin == null) {
+ throw new SecurityException("No active admin: " + activeAdmin);
+ }
+ if (activeAdmin.getUid() != mInjector.binderGetCallingUid()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ }
return activeAdmin.userRestrictions;
}
}
@@ -5945,9 +6131,9 @@
@Override
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
- Intent originalIntent) {
+ long actualDirectoryId, Intent originalIntent) {
final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
- actualLookupKey, actualContactId, originalIntent);
+ actualLookupKey, actualContactId, actualDirectoryId, originalIntent);
final int callingUserId = UserHandle.getCallingUserId();
final long ident = mInjector.binderClearCallingIdentity();
@@ -6060,7 +6246,7 @@
// Store the settings persistently.
saveSettingsLocked(userHandle);
- updateLockTaskPackages(packages, userHandle);
+ updateLockTaskPackagesLocked(packages, userHandle);
}
/**
@@ -6117,9 +6303,8 @@
Bundle adminExtras = new Bundle();
adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
for (ActiveAdmin admin : policy.mAdminList) {
- boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
- boolean ownsProfile = (getProfileOwner(userHandle) != null
- && getProfileOwner(userHandle).equals(admin.info.getPackageName()));
+ final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userHandle);
+ final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userHandle);
if (ownsDevice || ownsProfile) {
if (isEnabled) {
sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_ENTERING,
@@ -6171,13 +6356,12 @@
@Override
public void setSecureSetting(ComponentName who, String setting, String value) {
Preconditions.checkNotNull(who, "ComponentName is null");
- int callingUserId = UserHandle.getCallingUserId();
- final ContentResolver contentResolver = mContext.getContentResolver();
+ int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (isDeviceOwner(who)) {
+ if (isDeviceOwner(who, mInjector.userHandleGetCallingUserId())) {
if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)) {
throw new SecurityException(String.format(
"Permission denial: Device owners cannot update %1$s", setting));
@@ -6397,47 +6581,6 @@
}
}
- @Override
- public Bundle getComposedUserRestrictions(int userId, Bundle inBundle) {
- synchronized (DevicePolicyManagerService.this) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-
- final Bundle deviceOwnerRestrictions =
- deviceOwner == null ? null : deviceOwner.userRestrictions;
- final Bundle profileOwnerRestrictions =
- profileOwner == null ? null : profileOwner.userRestrictions;
- final boolean cameraDisabled = getCameraDisabled(null, userId);
-
- if (deviceOwnerRestrictions == null && profileOwnerRestrictions == null
- && !cameraDisabled) {
- // No restrictions to merge.
- return inBundle;
- }
-
- final Bundle composed = new Bundle(inBundle);
- UserRestrictionsUtils.merge(composed, deviceOwnerRestrictions);
- UserRestrictionsUtils.merge(composed, profileOwnerRestrictions);
-
- // Also merge in the camera restriction.
- if (cameraDisabled) {
- composed.putBoolean(UserManager.DISALLOW_CAMERA, true);
- }
-
- return composed;
- }
- }
-
- @Override
- public boolean isDeviceAdminPackage(int userId, String packageName) {
- if (packageName == null) {
- return false;
- }
- synchronized (DevicePolicyManagerService.this) {
- return packageHasActiveAdminsLocked(packageName, userId);
- }
- }
-
private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
synchronized (DevicePolicyManagerService.this) {
@@ -6499,13 +6642,26 @@
* @param callerUid UID of the caller.
* @return true if the caller is the device owner app
*/
- private boolean isCallerDeviceOwner(int callerUid) {
- String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
- for (String pkg : pkgs) {
- if (isDeviceOwnerPackage(pkg)) {
- return true;
+ @VisibleForTesting
+ boolean isCallerDeviceOwner(int callerUid) {
+ synchronized (this) {
+ if (!mOwners.hasDeviceOwner()) {
+ return false;
+ }
+ if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
+ return false;
+ }
+ final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
+ .getPackageName();
+ final String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
+
+ for (String pkg : pkgs) {
+ if (deviceOwnerPackageName.equals(pkg)) {
+ return true;
+ }
}
}
+
return false;
}
@@ -6524,8 +6680,9 @@
updateReceivedTime);
synchronized (this) {
- final String deviceOwnerPackage = getDeviceOwner() == null ? null :
- getDeviceOwner().getPackageName();
+ final String deviceOwnerPackage =
+ mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerComponent().getPackageName()
+ : null;
if (deviceOwnerPackage == null) {
return;
}
@@ -6585,10 +6742,8 @@
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
long ident = mInjector.binderClearCallingIdentity();
try {
- final ApplicationInfo ai = mIPackageManager
- .getApplicationInfo(packageName, 0, user.getIdentifier());
- final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
- if (targetSdkVersion < android.os.Build.VERSION_CODES.M) {
+ if (getTargetSdk(packageName, user.getIdentifier())
+ < android.os.Build.VERSION_CODES.M) {
return false;
}
final PackageManager packageManager = mContext.getPackageManager();
@@ -6662,11 +6817,28 @@
@Override
public boolean isProvisioningAllowed(String action) {
- if (mOwners.hasDeviceOwner()) {
- return false;
- }
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
+ if (mOwners.hasDeviceOwner()) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // Only split-system-user systems support managed-profiles in combination with
+ // device-owner.
+ return false;
+ }
+ if (mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM) {
+ // Only system device-owner supports managed-profiles. Non-system device-owner
+ // doesn't.
+ return false;
+ }
+ if (callingUserId == UserHandle.USER_SYSTEM) {
+ // Managed-profiles cannot be setup on the system user, only regular users.
+ return false;
+ }
+ }
+ if (getProfileOwner(callingUserId) != null) {
+ // Managed user cannot have a managed profile.
+ return false;
+ }
try {
if (!mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
return false;
@@ -6684,23 +6856,96 @@
}
return true;
} else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
- if (getProfileOwner(callingUserId) != null) {
- return false;
- }
- if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
- return false;
- }
- if (callingUserId != UserHandle.USER_SYSTEM) {
- // Device owner provisioning can only be initiated from system user.
- return false;
- }
- return true;
+ return isDeviceOwnerProvisioningAllowed(callingUserId);
} else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_USER.equals(action)) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // ACTION_PROVISION_MANAGED_USER only supported on split-user systems.
+ return false;
+ }
if (hasUserSetupCompleted(callingUserId)) {
return false;
}
return true;
+ } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE.equals(action)) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE only supported on split-user systems.
+ return false;
+ }
+ return isDeviceOwnerProvisioningAllowed(callingUserId);
}
throw new IllegalArgumentException("Unknown provisioning action " + action);
}
+
+ private boolean isDeviceOwnerProvisioningAllowed(int callingUserId) {
+ if (mOwners.hasDeviceOwner()) {
+ return false;
+ }
+ if (getProfileOwner(callingUserId) != null) {
+ return false;
+ }
+ if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+ return false;
+ }
+ if (callingUserId != UserHandle.USER_SYSTEM) {
+ // Device owner provisioning can only be initiated from system user.
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String getWifiMacAddress() {
+ // Make sure caller has DO.
+ synchronized (this) {
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ final WifiInfo wifiInfo = mInjector.getWifiManager().getConnectionInfo();
+ if (wifiInfo == null) {
+ return null;
+ }
+ return wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Returns the target sdk version number that the given packageName was built for
+ * in the given user.
+ */
+ private int getTargetSdk(String packageName, int userId) throws RemoteException {
+ final ApplicationInfo ai = mIPackageManager
+ .getApplicationInfo(packageName, 0, userId);
+ final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
+ return targetSdkVersion;
+ }
+
+ @Override
+ public boolean isManagedProfile(ComponentName admin) {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ final UserInfo user;
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ user = mUserManager.getUserInfo(callingUserId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ return user != null && user.isManagedProfile();
+ }
+
+ @Override
+ public boolean isSystemOnlyUser(ComponentName admin) {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
+ }
+
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index ded4422..435de7a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -221,6 +221,10 @@
return mDeviceOwner != null;
}
+ boolean isDeviceOwnerUserId(int userId) {
+ return mDeviceOwner != null && mDeviceOwnerUserId == userId;
+ }
+
boolean hasProfileOwner(int userId) {
return getProfileOwnerComponent(userId) != null;
}
@@ -625,20 +629,30 @@
}
public void dump(String prefix, PrintWriter pw) {
+ boolean needBlank = false;
if (mDeviceOwner != null) {
pw.println(prefix + "Device Owner: ");
mDeviceOwner.dump(prefix + " ", pw);
pw.println(prefix + " User ID: " + mDeviceOwnerUserId);
- pw.println();
+ needBlank = true;
}
if (mSystemUpdatePolicy != null) {
+ if (needBlank) {
+ needBlank = false;
+ pw.println();
+ }
pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
- pw.println();
+ needBlank = true;
}
if (mProfileOwners != null) {
for (Map.Entry<Integer, OwnerInfo> entry : mProfileOwners.entrySet()) {
+ if (needBlank) {
+ needBlank = false;
+ pw.println();
+ }
pw.println(prefix + "Profile Owner (User " + entry.getKey() + "): ");
entry.getValue().dump(prefix + " ", pw);
+ needBlank = true;
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e32af5c..2f33d7c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -407,8 +407,6 @@
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
- // Update after UsageStatsService is available, needed before performBootDexOpt.
- mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
mSystemServiceManager.startService(WebViewUpdateService.class);
@@ -612,11 +610,11 @@
// as appropriate.
mSystemServiceManager.startService(UiModeManagerService.class);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformBootDexOpt");
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformFstrimIfNeeded");
try {
- mPackageManagerService.performBootDexOpt();
+ mPackageManagerService.performFstrimIfNeeded();
} catch (Throwable e) {
- reportWtf("performing boot dexopt", e);
+ reportWtf("performing fstrim", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -1319,4 +1317,4 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
Slog.i(TAG, name);
}
-}
\ No newline at end of file
+}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index c9efc69..e7e99c4 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -22,8 +22,6 @@
import com.android.internal.util.StateMachine;
import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -145,10 +143,10 @@
// State variables.
private final StateMachine mController;
- private final PendingIntent mKickIntent;
- private final PendingIntent mTimeoutIntent;
- private final PendingIntent mRenewIntent;
- private final PendingIntent mOneshotTimeoutIntent;
+ private final AlarmListener mKickAlarm;
+ private final AlarmListener mTimeoutAlarm;
+ private final AlarmListener mRenewAlarm;
+ private final AlarmListener mOneshotTimeoutAlarm;
private final String mIfaceName;
private boolean mRegisteredForPreDhcpNotification;
@@ -206,16 +204,15 @@
mRandom = new Random();
// Used to schedule packet retransmissions.
- mKickIntent = createStateMachineCommandIntent("KICK", CMD_KICK);
+ mKickAlarm = new AlarmListener("KICK", CMD_KICK);
// Used to time out PacketRetransmittingStates.
- mTimeoutIntent = createStateMachineCommandIntent("TIMEOUT", CMD_TIMEOUT);
+ mTimeoutAlarm = new AlarmListener("TIMEOUT", CMD_TIMEOUT);
// Used to schedule DHCP renews.
- mRenewIntent = createStateMachineCommandIntent("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+ mRenewAlarm = new AlarmListener("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
// Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
// TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
// remove this.
- mOneshotTimeoutIntent = createStateMachineCommandIntent("ONESHOT_TIMEOUT",
- CMD_ONESHOT_TIMEOUT);
+ mOneshotTimeoutAlarm = new AlarmListener("ONESHOT_TIMEOUT", CMD_ONESHOT_TIMEOUT);
}
@Override
@@ -231,39 +228,29 @@
}
/**
- * Constructs a PendingIntent that sends the specified command to the state machine. This is
- * implemented by creating an Intent with the specified parameters, and creating and registering
- * a BroadcastReceiver for it. The broadcast must be sent by a process that holds the
- * {@code CONNECTIVITY_INTERNAL} permission.
- *
- * @param cmdName the name of the command. The intent's action will be
- * {@code android.net.dhcp.DhcpClient.<mIfaceName>.<cmdName>}
- * @param cmd the command to send to the state machine when the PendingIntent is triggered.
- * @return the PendingIntent
+ * An AlarmListener that sends the specified command to the state machine.
*/
- private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
- String action = DhcpClient.class.getName() + "." + mIfaceName + "." + cmdName;
+ private class AlarmListener implements AlarmManager.OnAlarmListener {
+ private final int cmd;
+ private final String name;
- Intent intent = new Intent(action, null).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- // TODO: The intent's package covers the whole of the system server, so it's pretty generic.
- // Consider adding some sort of token as well.
- intent.setPackage(mContext.getPackageName());
- PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, cmd, intent, 0);
+ public AlarmListener(final String cmdName, final int cmd) {
+ this.cmd = cmd;
+ this.name = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
+ }
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- sendMessage(cmd);
- }
- },
- new IntentFilter(action),
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- null);
+ public void set(long alarmTime) {
+ mAlarmManager.setExact(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, name, this, getHandler());
+ }
- return pendingIntent;
+ public void cancel() {
+ mAlarmManager.cancel(this);
+ }
+
+ public void onAlarm() {
+ sendMessage(cmd);
+ }
}
private boolean initInterface() {
@@ -425,11 +412,11 @@
}
private void scheduleRenew() {
- mAlarmManager.cancel(mRenewIntent);
+ mAlarmManager.cancel(mRenewAlarm);
if (mDhcpLeaseExpiry != 0) {
long now = SystemClock.elapsedRealtime();
long alarmTime = (now + mDhcpLeaseExpiry) / 2;
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mRenewIntent);
+ mRenewAlarm.set(alarmTime);
Log.d(TAG, "Scheduling renewal in " + ((alarmTime - now) / 1000) + "s");
} else {
Log.d(TAG, "Infinite lease, no renewal needed");
@@ -561,12 +548,7 @@
// one state, so we can just use the state timeout.
private void scheduleOneshotTimeout() {
final long alarmTime = SystemClock.elapsedRealtime() + DHCP_TIMEOUT_MS;
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
- mOneshotTimeoutIntent);
- }
-
- private void cancelOneshotTimeout() {
- mAlarmManager.cancel(mOneshotTimeoutIntent);
+ mOneshotTimeoutAlarm.set(alarmTime);
}
class StoppedState extends LoggingState {
@@ -618,7 +600,7 @@
@Override
public void exit() {
- cancelOneshotTimeout();
+ mOneshotTimeoutAlarm.cancel();
if (mReceiveThread != null) {
mReceiveThread.halt(); // Also closes sockets.
mReceiveThread = null;
@@ -709,8 +691,8 @@
}
public void exit() {
- mAlarmManager.cancel(mKickIntent);
- mAlarmManager.cancel(mTimeoutIntent);
+ mKickAlarm.cancel();
+ mTimeoutAlarm.cancel();
}
abstract protected boolean sendPacket();
@@ -731,8 +713,8 @@
long now = SystemClock.elapsedRealtime();
long timeout = jitterTimer(mTimer);
long alarmTime = now + timeout;
- mAlarmManager.cancel(mKickIntent);
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mKickIntent);
+ mKickAlarm.cancel();
+ mKickAlarm.set(alarmTime);
mTimer *= 2;
if (mTimer > MAX_TIMEOUT_MS) {
mTimer = MAX_TIMEOUT_MS;
@@ -742,8 +724,7 @@
protected void maybeInitTimeout() {
if (mTimeout > 0) {
long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
- mAlarmManager.setExact(
- AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mTimeoutIntent);
+ mTimeoutAlarm.set(alarmTime);
}
}
}
@@ -843,7 +824,7 @@
@Override
public void enter() {
super.enter();
- cancelOneshotTimeout();
+ mOneshotTimeoutAlarm.cancel();
notifySuccess();
// TODO: DhcpStateMachine only supports renewing at 50% of the lease time, and does not
// support rebinding. Once the legacy DHCP client is gone, fix this.
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 8927bfa..6a255e5 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -291,6 +291,11 @@
*/
abstract void finishPacket(ByteBuffer buffer);
+ // Set in unit tests, to ensure that the test does not break when run on different devices and
+ // on different releases.
+ static String testOverrideVendorId = null;
+ static String testOverrideHostname = null;
+
protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
Inet4Address nextIp, Inet4Address relayIp,
byte[] clientMac, boolean broadcast) {
@@ -593,6 +598,16 @@
buf.put((byte) 0xFF);
}
+ private String getVendorId() {
+ if (testOverrideVendorId != null) return testOverrideVendorId;
+ return "android-dhcp-" + Build.VERSION.RELEASE;
+ }
+
+ private String getHostname() {
+ if (testOverrideHostname != null) return testOverrideHostname;
+ return SystemProperties.get("net.hostname");
+ }
+
/**
* Adds common client TLVs.
*
@@ -601,8 +616,8 @@
*/
protected void addCommonClientTlvs(ByteBuffer buf) {
addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
- addTlv(buf, DHCP_VENDOR_CLASS_ID, "android-dhcp-" + Build.VERSION.RELEASE);
- addTlv(buf, DHCP_HOST_NAME, SystemProperties.get("net.hostname"));
+ addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId());
+ addTlv(buf, DHCP_HOST_NAME, getHostname());
}
/**
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f37bb9eb..6a50a6e 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -343,9 +343,7 @@
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
- if (mActiveServices.isEmpty()) {
- return;
- }
+
if (mPrinterDiscoverySession == null) {
// If we do not have a session, tell all service to create one.
mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index c147bcc..eed326e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -94,6 +94,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
</application>
<instrumentation
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 7e60bf1..2a967e6 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -29,6 +29,7 @@
import junit.framework.TestCase;
import libcore.util.HexEncoding;
+import java.util.Arrays;
import static android.net.dhcp.DhcpPacket.*;
@@ -47,6 +48,11 @@
return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
}
+ public void setUp() {
+ DhcpPacket.testOverrideVendorId = "android-dhcp-???";
+ DhcpPacket.testOverrideHostname = "android-01234567890abcde";
+ }
+
class TestDhcpPacket extends DhcpPacket {
private byte mType;
// TODO: Make this a map of option numbers to bytes instead.
@@ -584,4 +590,93 @@
assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
null, "192.171.189.2", null, 28800, false, dhcpResults);
}
+
+ @SmallTest
+ public void testDiscoverPacket() throws Exception {
+ short secs = 7;
+ int transactionId = 0xdeadbeef;
+ byte[] hwaddr = {
+ (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
+ };
+ byte[] params = new byte[] {
+ DHCP_SUBNET_MASK,
+ DHCP_ROUTER,
+ DHCP_DNS_SERVER,
+ DHCP_DOMAIN_NAME,
+ DHCP_MTU,
+ DHCP_LEASE_TIME,
+ };
+
+ ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
+ DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
+ false /* do unicast */, params);
+
+ byte[] headers = new byte[] {
+ // Ethernet header.
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
+ (byte) 0x08, (byte) 0x00,
+ // IP header.
+ (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x52,
+ (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
+ (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x8c,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // UDP header.
+ (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
+ (byte) 0x01, (byte) 0x3e, (byte) 0xd8, (byte) 0xa4,
+ // BOOTP.
+ (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
+ (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
+ (byte) 0xb1, (byte) 0x7a
+ };
+ byte[] options = new byte[] {
+ // Magic cookie 0x63825363.
+ (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
+ // Message type DISCOVER.
+ (byte) 0x35, (byte) 0x01, (byte) 0x01,
+ // Client identifier Ethernet, da:01:19:5b:b1:7a.
+ (byte) 0x3d, (byte) 0x07,
+ (byte) 0x01,
+ (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
+ // Max message size 1500.
+ (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
+ // Version "android-dhcp-???".
+ (byte) 0x3c, (byte) 0x10,
+ 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
+ // Hostname "android-01234567890abcde"
+ (byte) 0x0c, (byte) 0x18,
+ 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
+ // Requested parameter list.
+ (byte) 0x37, (byte) 0x06,
+ DHCP_SUBNET_MASK,
+ DHCP_ROUTER,
+ DHCP_DNS_SERVER,
+ DHCP_DOMAIN_NAME,
+ DHCP_MTU,
+ DHCP_LEASE_TIME,
+ // End options.
+ (byte) 0xff,
+ // Our packets are always of even length. TODO: find out why and possibly fix it.
+ (byte) 0x00
+ };
+ byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
+ assertTrue((expected.length & 1) == 0);
+ System.arraycopy(headers, 0, expected, 0, headers.length);
+ System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
+
+ byte[] actual = new byte[packet.limit()];
+ packet.get(actual);
+ String msg =
+ "Expected:\n " + Arrays.toString(expected) +
+ "\nActual:\n " + Arrays.toString(actual);
+ assertTrue(msg, Arrays.equals(expected, actual));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index b0296a0..c174a92 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -61,7 +61,7 @@
b2.putBoolean("b2", true);
SyncOperation op1 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -73,7 +73,7 @@
// Same as op1 but different time infos
SyncOperation op2 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -85,7 +85,7 @@
// Same as op1 but different authority
SyncOperation op3 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority2",
b1,
@@ -97,7 +97,7 @@
// Same as op1 but different account
SyncOperation op4 = new SyncOperation(account2, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
@@ -109,7 +109,7 @@
// Same as op1 but different bundle
SyncOperation op5 = new SyncOperation(account1, 0,
- 1,
+ 1, "foo", 0,
SyncOperation.REASON_PERIODIC,
"authority1",
b2,
@@ -131,21 +131,21 @@
long soonFlex = 50;
long after = 1500;
long afterFlex = 100;
- SyncOperation op1 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op1 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval disjoint from and after op1.
- SyncOperation op2 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op2 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
// Interval equivalent to op1, but expedited.
Bundle b2 = new Bundle();
b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op3 = new SyncOperation(mDummy, 0, 0, 0,
+ SyncOperation op3 = new SyncOperation(mDummy, 0, 0, "foo", 0, 0,
"authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
// Interval overlaps but not equivalent to op1.
- SyncOperation op4 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+ SyncOperation op4 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
"authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
assertTrue(op1.compareTo(op2) == -1);
@@ -165,7 +165,8 @@
Bundle withExpedited = new Bundle();
withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_USER_START,
+ SyncOperation op = new SyncOperation(mDummy, 0, 0, "foo", 0,
+ SyncOperation.REASON_USER_START,
mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
// Create another sync op to be rerun in 5 minutes.
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index ae1967e..b22eb53 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -41,8 +41,6 @@
import java.io.FileOutputStream;
import java.util.List;
-import com.android.server.content.SyncStorageEngine.EndPoint;
-
public class SyncStorageEngineTest extends AndroidTestCase {
protected Account account1;
@@ -96,7 +94,7 @@
SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
new TestContext(mockResolver, getContext()));
long time0 = 1000;
- SyncOperation op = new SyncOperation(account, 0,
+ SyncOperation op = new SyncOperation(account, 0, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL,
authority,
@@ -112,7 +110,7 @@
@MediumTest
public void testAppendPending() throws Exception {
SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */,
@@ -140,19 +138,19 @@
*/
public void testWritePendingOperationsLocked() throws Exception {
SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_IS_SYNCABLE,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
1000L /* runtime */, 57L /* flex */, 0 /* backoff */, 0 /* delayuntil */,
true /* expedited */);
SyncOperation sop1 = new SyncOperation(account2,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL, authority1, defaultBundle,
0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
false /* expedited */);
SyncOperation deleted = new SyncOperation(account2,
- DEFAULT_USER,
+ DEFAULT_USER, 0, "foo",
SyncOperation.REASON_SYNC_AUTO,
SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
@@ -456,14 +454,14 @@
// Test service component read
List<PeriodicSync> syncs = engine.getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(syncService1, 0));
+ new SyncStorageEngine.EndPoint(syncService1, 0, 0));
assertEquals(1, syncs.size());
assertEquals(true, engine.getIsTargetServiceActive(syncService1, 0));
}
@SmallTest
public void testComponentSettings() throws Exception {
- EndPoint target1 = new EndPoint(syncService1, 0);
+ EndPoint target1 = new EndPoint(syncService1, 0, 0);
engine.updateOrAddPeriodicSync(target1, dayPoll, dayFuzz, Bundle.EMPTY);
engine.setIsTargetServiceActive(target1.service, 0, true);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index dfa9f8f..f32f209 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -145,16 +145,13 @@
// Check the new base restrictions.
DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_RECORD_AUDIO
- ),
+ DpmTestUtils.newRestrictions(),
newBaseRestrictions.get(UserHandle.USER_SYSTEM));
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
UserManager.DISALLOW_SMS,
- UserManager.DISALLOW_OUTGOING_CALLS,
- UserManager.DISALLOW_RECORD_AUDIO
+ UserManager.DISALLOW_OUTGOING_CALLS
),
newBaseRestrictions.get(10));
@@ -162,28 +159,30 @@
DpmTestUtils.newRestrictions(
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_OUTGOING_CALLS,
- UserManager.DISALLOW_WALLPAPER,
- UserManager.DISALLOW_RECORD_AUDIO
+ UserManager.DISALLOW_WALLPAPER
),
newBaseRestrictions.get(11));
// Check the new owner restrictions.
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_ADD_USER
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_RECORD_AUDIO
),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions());
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
UserManager.DISALLOW_REMOVE_USER,
- UserManager.DISALLOW_WALLPAPER
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_RECORD_AUDIO
),
dpms.getProfileOwnerAdminLocked(10).ensureUserRestrictions());
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_REMOVE_USER
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_RECORD_AUDIO
),
dpms.getProfileOwnerAdminLocked(11).ensureUserRestrictions());
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 6419338..56d6fc0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -20,8 +20,8 @@
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
-import android.content.Context;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.media.IAudioService;
import android.os.Looper;
import android.os.PowerManagerInternal;
@@ -113,6 +113,11 @@
}
@Override
+ PackageManagerInternal getPackageManagerInternal() {
+ return context.packageManagerInternal;
+ }
+
+ @Override
PowerManagerInternal getPowerManagerInternal() {
return context.powerManagerInternal;
}
@@ -128,7 +133,7 @@
}
@Override
- IActivityManager getIActivityManagerInner() {
+ IActivityManager getIActivityManager() {
return context.iactivityManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index ca3d950..565ef4b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -25,20 +25,22 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.net.wifi.WifiInfo;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.content.pm.PackageInfo;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.test.MoreAsserts;
import android.util.Pair;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -50,6 +52,7 @@
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -59,9 +62,9 @@
*
m FrameworksServicesTests &&
adb install \
- -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
- -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
@@ -84,6 +87,7 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(adminNoPerm, DpmMockContext.CALLER_UID);
setUpUserManager();
}
@@ -337,6 +341,33 @@
/**
* Test for:
+ * {@link DevicePolicyManager#setActiveAdmin} when the admin isn't protected with
+ * BIND_DEVICE_ADMIN.
+ */
+ public void testSetActiveAdmin_permissionCheck() throws Exception {
+ // 1. Make sure the caller has proper permissions.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ try {
+ dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().contains(permission.BIND_DEVICE_ADMIN));
+ }
+ assertFalse(dpm.isAdminActive(adminNoPerm));
+
+ // Change the target API level to MNC. Now it can be set as DA.
+ setUpPackageManagerForAdmin(adminNoPerm, DpmMockContext.CALLER_UID, null,
+ VERSION_CODES.M);
+ dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(adminNoPerm));
+
+ // TODO Test the "load from the file" case where DA will still be loaded even without
+ // BIND_DEVICE_ADMIN and target API is N.
+ }
+
+ /**
+ * Test for:
* {@link DevicePolicyManager#removeActiveAdmin}
*/
public void testRemoveActiveAdmin_SecurityException() {
@@ -461,6 +492,7 @@
*/
public void testSetDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -470,12 +502,33 @@
// Make sure admin1 is installed on system user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ // Check various get APIs.
+ checkGetDeviceOwnerInfoApi(dpm, /* hasDeviceOwner =*/ false);
+
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+ // getDeviceOwnerComponent should return the admin1 component.
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+
+ // Check various get APIs.
+ checkGetDeviceOwnerInfoApi(dpm, /* hasDeviceOwner =*/ true);
+
+ // getDeviceOwnerComponent should *NOT* return the admin1 component for other users.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
// TODO We should check if the caller has called clearCallerIdentity().
verify(mContext.ibackupManager, times(1)).setBackupServiceActive(
eq(UserHandle.USER_SYSTEM), eq(false));
@@ -484,7 +537,7 @@
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Try to set a profile owner on the same user, which should fail.
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
@@ -501,11 +554,163 @@
// DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
+ private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) {
+ final int origCallingUser = mContext.binder.callingUid;
+ final List origPermissions = new ArrayList(mContext.callerPermissions);
+ mContext.callerPermissions.clear();
+
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
+
+ mContext.binder.callingUid = Process.SYSTEM_UID;
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Still with MANAGE_USERS.
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = Process.SYSTEM_UID;
+ mContext.callerPermissions.remove(permission.MANAGE_USERS);
+ // System can still call "OnAnyUser" without MANAGE_USERS.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ // Still no MANAGE_USERS.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+ }
+
+ try {
+ dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerComponentOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerUserId();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerNameOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Still no MANAGE_USERS.
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ try {
+ dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerComponentOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerUserId();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerNameOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ // Restore.
+ mContext.binder.callingUid = origCallingUser;
+ mContext.callerPermissions.addAll(origPermissions);
+ }
+
+
/**
* Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist.
*/
public void testSetDeviceOwner_noSuchPackage() {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -527,6 +732,7 @@
public void testClearDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -542,7 +748,11 @@
dpm.setActiveAdmin(admin1, /* replace =*/ false);
assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Set up other mocks.
when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
@@ -554,13 +764,14 @@
dpm.clearDeviceOwnerApp(admin1.getPackageName());
// Now DO shouldn't be set.
- assertNull(dpm.getDeviceOwner());
+ assertNull(dpm.getDeviceOwnerComponentOnAnyUser());
// TODO Check other calls.
}
public void testClearDeviceOwner_fromDifferentUser() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -576,7 +787,11 @@
dpm.setActiveAdmin(admin1, /* replace =*/ false);
assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Now call clear from the secondary user, which should throw.
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -592,8 +807,8 @@
assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
}
- // Now DO shouldn't be set.
- assertNotNull(dpm.getDeviceOwner());
+ // DO shouldn't be removed.
+ assertTrue(dpm.isDeviceManaged());
}
public void testSetProfileOwner() throws Exception {
@@ -630,6 +845,7 @@
mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -658,8 +874,7 @@
mContext.setUserRunning(DpmMockContext.CALLER_USER_HANDLE, true);
assertTrue(dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
- // Make sure it's set.
- assertEquals(admin2, dpm.getDeviceOwnerComponent());
+ assertEquals(admin2, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false));
// Then check getDeviceOwnerAdminLocked().
assertEquals(admin2, dpms.getDeviceOwnerAdminLocked().info.getComponent());
@@ -683,7 +898,7 @@
dpms.mOwners.writeDeviceOwner();
// Make sure the DO component name doesn't have a class name.
- assertEquals("", dpms.getDeviceOwner().getClassName());
+ assertEquals("", dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false).getClassName());
// Then create a new DPMS to have it load the settings from files.
when(mContext.userManager.getUserRestrictions(any(UserHandle.class)))
@@ -693,7 +908,7 @@
// Now the DO component name is a full name.
// *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
// DO.
- assertEquals(admin1, dpms.getDeviceOwner());
+ assertEquals(admin1, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false));
}
public void testSetGetApplicationRestriction() {
@@ -731,6 +946,7 @@
public void testSetUserRestriction_asDo() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -756,21 +972,42 @@
dpm.getUserRestrictions(admin1)
);
- dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS);
+ reset(mContext.userManagerInternal);
+
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
+
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
+ UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_OUTGOING_CALLS),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_SMS, UserManager.DISALLOW_OUTGOING_CALLS),
+ UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_OUTGOING_CALLS),
dpm.getUserRestrictions(admin1)
);
- dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS);
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ MockUtils.checkUserRestrictions()
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
@@ -782,6 +1019,12 @@
);
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions()
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
@@ -792,7 +1035,68 @@
dpm.getUserRestrictions(admin1)
);
- // TODO Check inner calls.
+ // DISALLOW_ADJUST_VOLUME and DISALLOW_UNMUTE_MICROPHONE are PO restrictions, but when
+ // DO sets them, the scope is global.
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
+ reset(mContext.userManagerInternal);
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE)
+ );
+ reset(mContext.userManagerInternal);
+
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+
+
+ // More tests.
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
+
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
+ UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
+
+ dpm.setCameraDisabled(admin1, true);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ // DISALLOW_CAMERA will be applied to both local and global.
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
+ UserManager.DISALLOW_CAMERA, UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
+
+ // Set up another DA and let it disable camera. Now DISALLOW_CAMERA will only be applied
+ // locally.
+ dpm.setCameraDisabled(admin1, false);
+ reset(mContext.userManagerInternal);
+
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin2, /* replace =*/ false, UserHandle.USER_SYSTEM);
+ dpm.setCameraDisabled(admin2, true);
+
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(UserHandle.USER_SYSTEM),
+ // DISALLOW_CAMERA will be applied to both local and global.
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
+ UserManager.DISALLOW_ADD_USER)
+ );
+ reset(mContext.userManagerInternal);
// TODO Make sure restrictions are written to the file.
}
@@ -806,7 +1110,21 @@
);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
+
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_OUTGOING_CALLS),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
@@ -825,7 +1143,12 @@
);
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
-
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
@@ -842,6 +1165,12 @@
);
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
@@ -853,69 +1182,89 @@
dpm.getUserRestrictions(admin1)
);
- // TODO Check inner calls.
+ // DISALLOW_ADJUST_VOLUME and DISALLOW_UNMUTE_MICROPHONE can be set by PO too, even
+ // though when DO sets them they'll be applied globally.
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
+ reset(mContext.userManagerInternal);
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
+
+ dpm.setCameraDisabled(admin1, true);
+ verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA,
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE),
+ isNull(Bundle.class)
+ );
+ reset(mContext.userManagerInternal);
+
// TODO Make sure restrictions are written to the file.
}
- public void testGetComposedUserRestrictions_noDoNoPo() throws Exception {
- final Bundle in = DpmTestUtils.newRestrictions(UserManager.DISALLOW_OUTGOING_CALLS);
-
- Bundle actual = dpms.mLocalService.getComposedUserRestrictions(
- UserHandle.USER_SYSTEM, in);
- assertTrue(in == actual);
-
- actual = dpms.mLocalService.getComposedUserRestrictions(
- DpmMockContext.CALLER_USER_HANDLE, in);
- assertTrue(in == actual);
- }
-
- public void testGetComposedUserRestrictions() throws Exception {
+ public void testGetMacAddress() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- // First, set DO.
-
- // Call from a process on the system user.
+ // In this test, change the caller user to "system".
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
// Make sure admin1 is installed on system user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
- // Call.
- dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
- UserHandle.USER_SYSTEM));
+ // Test 1. Caller doesn't have DO or DA.
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
- dpm.addUserRestriction(admin1, "rest1");
- dpm.addUserRestriction(admin1, "rest2");
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(admin1));
- // Set PO on CALLER_USER_HANDLE.
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Test 2. Caller has DA, but not DO.
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
- setAsProfileOwner(admin2);
+ // Test 3. Caller has PO, but not DO.
+ assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
- dpm.addUserRestriction(admin2, "restA");
- dpm.addUserRestriction(admin2, "restB");
+ // Remove PO.
+ dpm.clearProfileOwner(admin1);
- final Bundle in = DpmTestUtils.newRestrictions("abc");
+ // Test 4, Caller is DO now.
+ assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
- Bundle actual = dpms.mLocalService.getComposedUserRestrictions(
- UserHandle.USER_SYSTEM, in);
- DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions("abc", "rest1", "rest2"),
- actual);
+ // 4-1. But no WifiInfo.
+ assertNull(dpm.getWifiMacAddress());
- actual = dpms.mLocalService.getComposedUserRestrictions(
- DpmMockContext.CALLER_USER_HANDLE, in);
- DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions("abc", "rest1", "rest2", "restA", "restB"),
- actual);
+ // 4-2. Returns WifiInfo, but with the default MAC.
+ when(mContext.wifiManager.getConnectionInfo()).thenReturn(new WifiInfo());
+ assertNull(dpm.getWifiMacAddress());
- actual = dpms.mLocalService.getComposedUserRestrictions(
- DpmMockContext.CALLER_USER_HANDLE + 1, in);
- DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions("abc", "rest1", "rest2"),
- actual);
+ // 4-3. With a real MAC address.
+ final WifiInfo wi = new WifiInfo();
+ wi.setMacAddress("11:22:33:44:55:66");
+ when(mContext.wifiManager.getConnectionInfo()).thenReturn(wi);
+ assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index cc337b0..66d701d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -28,8 +28,10 @@
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.media.IAudioService;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager.WakeLock;
@@ -205,6 +207,7 @@
public final SystemPropertiesForMock systemProperties;
public final UserManager userManager;
public final UserManagerInternal userManagerInternal;
+ public final PackageManagerInternal packageManagerInternal;
public final UserManagerForMock userManagerForMock;
public final PowerManagerForMock powerManager;
public final PowerManagerInternal powerManagerInternal;
@@ -215,6 +218,7 @@
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
+ public final WifiManager wifiManager;
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
@@ -237,6 +241,7 @@
userManager = mock(UserManager.class);
userManagerInternal = mock(UserManagerInternal.class);
userManagerForMock = mock(UserManagerForMock.class);
+ packageManagerInternal = mock(PackageManagerInternal.class);
powerManager = mock(PowerManagerForMock.class);
powerManagerInternal = mock(PowerManagerInternal.class);
notificationManager = mock(NotificationManager.class);
@@ -246,6 +251,7 @@
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
+ wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
// Package manager is huge, so we use a partial mock instead.
@@ -260,9 +266,6 @@
// System user is always running.
setUserRunning(UserHandle.USER_SYSTEM, true);
-
- // This method must return an object.
- when(userManagerInternal.getUserRestrictionsLock()).thenReturn(new Object());
}
public File addUser(int userId, int flags) {
@@ -303,6 +306,8 @@
return userManager;
case Context.POWER_SERVICE:
return powerManager;
+ case Context.WIFI_SERVICE:
+ return wifiManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index e11f3fb..5b33e4d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -43,6 +43,7 @@
public ComponentName admin1;
public ComponentName admin2;
public ComponentName admin3;
+ public ComponentName adminNoPerm;
@Override
protected void setUp() throws Exception {
@@ -56,6 +57,7 @@
admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
+ adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
}
@Override
@@ -67,11 +69,36 @@
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
throws Exception {
setUpPackageManagerForAdmin(admin, packageUid,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ /* enabledSetting =*/ null, /* appTargetSdk = */ null);
}
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
int enabledSetting) throws Exception {
+ setUpPackageManagerForAdmin(admin, packageUid, enabledSetting, /* appTargetSdk = */ null);
+ }
+
+ protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+ Integer enabledSetting, Integer appTargetSdk) throws Exception {
+
+ // Set up getApplicationInfo().
+
+ final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getApplicationInfo(
+ admin.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
+
+ ai.enabledSetting = enabledSetting == null
+ ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ : enabledSetting;
+ if (appTargetSdk != null) {
+ ai.targetSdkVersion = appTargetSdk;
+ }
+ ai.uid = packageUid;
+
+ doReturn(ai).when(mMockContext.ipackageManager).getApplicationInfo(
+ eq(admin.getPackageName()),
+ eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(UserHandle.getUserId(packageUid)));
// Set up queryBroadcastReceivers().
@@ -88,7 +115,7 @@
realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
// We need to rewrite the UID in the activity info.
- realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
+ realResolveInfo.get(0).activityInfo.applicationInfo = ai;
doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceivers(
MockUtils.checkIntentComponent(admin),
@@ -96,21 +123,6 @@
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
eq(UserHandle.getUserId(packageUid)));
- // Set up getApplicationInfo().
-
- final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
- mRealTestContext.getPackageManager().getApplicationInfo(
- admin.getPackageName(),
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
-
- ai.enabledSetting = enabledSetting;
- ai.uid = packageUid;
-
- doReturn(ai).when(mMockContext.ipackageManager).getApplicationInfo(
- eq(admin.getPackageName()),
- eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(UserHandle.getUserId(packageUid)));
-
// Set up getPackageInfo().
final PackageInfo pi = DpmTestUtils.cloneParcelable(
@@ -118,7 +130,7 @@
admin.getPackageName(), 0));
assertTrue(pi.applicationInfo.flags != 0);
- pi.applicationInfo.uid = packageUid;
+ pi.applicationInfo = ai;
doReturn(pi).when(mMockContext.ipackageManager).getPackageInfo(
eq(admin.getPackageName()),
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
index 08293a2..a0f4d97 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
@@ -24,4 +24,6 @@
}
public static class Admin3 extends DeviceAdminReceiver {
}
+ public static class AdminNoPerm extends DeviceAdminReceiver {
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 5008fbf..58db192 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -17,8 +17,12 @@
import com.google.common.base.Objects;
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.UserRestrictionsUtils;
+
import android.content.ComponentName;
import android.content.Intent;
+import android.os.Bundle;
import android.os.UserHandle;
import org.hamcrest.BaseMatcher;
@@ -77,4 +81,37 @@
};
return Mockito.argThat(m);
}
+
+ public static Bundle checkUserRestrictions(String... keys) {
+ final Bundle expected = DpmTestUtils.newRestrictions(Preconditions.checkNotNull(keys));
+ final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
+ @Override
+ public boolean matches(Object item) {
+ if (item == null) return false;
+ return UserRestrictionsUtils.areEqual((Bundle) item, expected);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("User restrictions=" + getRestrictionsAsString(expected));
+ }
+ };
+ return Mockito.argThat(m);
+ }
+
+ private static String getRestrictionsAsString(Bundle b) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[");
+
+ if (b != null) {
+ String sep = "";
+ for (String key : b.keySet()) {
+ sb.append(sep);
+ sep = ",";
+ sb.append(key);
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 4e11762..423c4d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -19,21 +19,7 @@
import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
import android.content.ComponentName;
-import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-
-import static org.mockito.Mockito.when;
/**
* Tests for the DeviceOwner object that saves & loads device and policy owner information.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
new file mode 100644
index 0000000..5542a4f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.server.devicepolicy.DpmTestUtils;
+
+import android.os.Bundle;
+import android.os.UserManager;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+/**
+ * Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
+ *
+ * <p>Run with:<pre>
+ m FrameworksServicesTests &&
+ adb install \
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.UserRestrictionsUtilsTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+public class UserRestrictionsUtilsTest extends AndroidTestCase {
+ public void testNonNull() {
+ Bundle out = UserRestrictionsUtils.nonNull(null);
+ assertNotNull(out);
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+
+ Bundle in = new Bundle();
+ assertSame(in, UserRestrictionsUtils.nonNull(in));
+ }
+
+ public void testIsEmpty() {
+ assertTrue(UserRestrictionsUtils.isEmpty(null));
+ assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
+ assertFalse(UserRestrictionsUtils.isEmpty(DpmTestUtils.newRestrictions("a")));
+ }
+
+ public void testClone() {
+ Bundle in = new Bundle();
+ Bundle out = UserRestrictionsUtils.clone(in);
+ assertNotSame(in, out);
+ DpmTestUtils.assertRestrictions(out, new Bundle());
+
+ out = UserRestrictionsUtils.clone(null);
+ assertNotNull(out);
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+ }
+
+ public void testMerge() {
+ Bundle a = DpmTestUtils.newRestrictions("a", "d");
+ Bundle b = DpmTestUtils.newRestrictions("b", "d", "e");
+
+ UserRestrictionsUtils.merge(a, b);
+
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+
+ UserRestrictionsUtils.merge(a, null);
+
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+
+ try {
+ UserRestrictionsUtils.merge(a, a);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testCanDeviceOwnerChange() {
+ assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
+ assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
+ assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
+ }
+
+ public void testCanProfileOwnerChange() {
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(UserManager.DISALLOW_WALLPAPER));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(UserManager.DISALLOW_ADD_USER));
+ assertTrue(UserRestrictionsUtils.canProfileOwnerChange(UserManager.DISALLOW_ADJUST_VOLUME));
+ }
+
+ public void testSortToGlobalAndLocal() {
+ final Bundle local = new Bundle();
+ final Bundle global = new Bundle();
+
+ UserRestrictionsUtils.sortToGlobalAndLocal(null, global, local);
+ assertEquals(0, global.size());
+ assertEquals(0, local.size());
+
+ UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, global, local);
+ assertEquals(0, global.size());
+ assertEquals(0, local.size());
+
+ UserRestrictionsUtils.sortToGlobalAndLocal(DpmTestUtils.newRestrictions(
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+ UserManager.DISALLOW_USB_FILE_TRANSFER,
+ UserManager.DISALLOW_CONFIG_TETHERING,
+ UserManager.DISALLOW_OUTGOING_BEAM,
+ UserManager.DISALLOW_APPS_CONTROL
+ ), global, local);
+
+
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+ // These can be set by PO too, but when DO sets them, they're global.
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+
+ // These can only be set by DO.
+ UserManager.DISALLOW_USB_FILE_TRANSFER,
+ UserManager.DISALLOW_CONFIG_TETHERING
+ ), global);
+
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+ // They can be set by both DO/PO.
+ UserManager.DISALLOW_OUTGOING_BEAM,
+ UserManager.DISALLOW_APPS_CONTROL
+ ), local);
+ }
+
+ public void testAreEqual() {
+ assertTrue(UserRestrictionsUtils.areEqual(
+ null,
+ null));
+
+ assertTrue(UserRestrictionsUtils.areEqual(
+ null,
+ Bundle.EMPTY));
+
+ assertTrue(UserRestrictionsUtils.areEqual(
+ Bundle.EMPTY,
+ null));
+
+ assertTrue(UserRestrictionsUtils.areEqual(
+ Bundle.EMPTY,
+ Bundle.EMPTY));
+
+ assertTrue(UserRestrictionsUtils.areEqual(
+ new Bundle(),
+ Bundle.EMPTY));
+
+ assertFalse(UserRestrictionsUtils.areEqual(
+ null,
+ DpmTestUtils.newRestrictions("a")));
+
+ assertFalse(UserRestrictionsUtils.areEqual(
+ DpmTestUtils.newRestrictions("a"),
+ null));
+
+ assertTrue(UserRestrictionsUtils.areEqual(
+ DpmTestUtils.newRestrictions("a"),
+ DpmTestUtils.newRestrictions("a")));
+
+ assertFalse(UserRestrictionsUtils.areEqual(
+ DpmTestUtils.newRestrictions("a"),
+ DpmTestUtils.newRestrictions("a", "b")));
+
+ assertFalse(UserRestrictionsUtils.areEqual(
+ DpmTestUtils.newRestrictions("a", "b"),
+ DpmTestUtils.newRestrictions("a")));
+
+ assertFalse(UserRestrictionsUtils.areEqual(
+ DpmTestUtils.newRestrictions("b", "a"),
+ DpmTestUtils.newRestrictions("a", "a")));
+
+ // Make sure false restrictions are handled correctly.
+ final Bundle a = DpmTestUtils.newRestrictions("a");
+ a.putBoolean("b", true);
+
+ final Bundle b = DpmTestUtils.newRestrictions("a");
+ b.putBoolean("b", false);
+
+ assertFalse(UserRestrictionsUtils.areEqual(a, b));
+ assertFalse(UserRestrictionsUtils.areEqual(b, a));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java b/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java
index b6742a1..d798518 100644
--- a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java
+++ b/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java
@@ -16,6 +16,8 @@
package com.android.server.updates;
+import com.android.internal.util.HexDump;
+
import android.content.Context;
import android.content.Intent;
import android.test.AndroidTestCase;
@@ -128,7 +130,7 @@
MessageDigest dgst = MessageDigest.getInstance("SHA512");
byte[] encoded = content.getBytes();
byte[] fingerprint = dgst.digest(encoded);
- return IntegralToString.bytesToHexString(fingerprint, false);
+ return HexDump.toHexString(fingerprint, false);
}
private static String getHashOfCurrentContent() throws Exception {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 013154b..2b0919b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -215,11 +215,11 @@
mPowerManager = getContext().getSystemService(PowerManager.class);
mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
- synchronized (this) {
+ synchronized (mLock) {
mScreenOnTime = readScreenOnTimeLocked();
}
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
- synchronized (this) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mLock) {
updateDisplayLocked();
}
} else if (phase == PHASE_BOOT_COMPLETED) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e0f95cf..c734fab 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -772,7 +772,8 @@
}
private void updateUsbNotification() {
- if (mNotificationManager == null || !mUseUsbNotification) return;
+ if (mNotificationManager == null || !mUseUsbNotification
+ || ("0".equals(SystemProperties.get("persist.charging.notify")))) return;
int id = 0;
Resources r = mContext.getResources();
if (mConnected || mHostConnected) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a96c164..8fee91f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -291,21 +291,26 @@
String curService = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
ComponentName serviceComponent = null;
+ ServiceInfo serviceInfo = null;
if (curService != null && !curService.isEmpty()) {
try {
serviceComponent = ComponentName.unflattenFromString(curService);
- } catch (RuntimeException e) {
+ serviceInfo = AppGlobals.getPackageManager()
+ .getServiceInfo(serviceComponent, 0, mCurUser);
+ } catch (RuntimeException | RemoteException e) {
Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
serviceComponent = null;
+ serviceInfo = null;
}
}
+
if (force || mImpl == null || mImpl.mUser != mCurUser
|| !mImpl.mComponent.equals(serviceComponent)) {
mSoundTriggerHelper.stopAllRecognitions();
if (mImpl != null) {
mImpl.shutdownLocked();
}
- if (serviceComponent != null) {
+ if (serviceComponent != null && serviceInfo != null) {
mImpl = new VoiceInteractionManagerServiceImpl(mContext,
UiThread.getHandler(), this, mCurUser, serviceComponent);
mImpl.startLocked();
@@ -357,7 +362,6 @@
}
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Failure looking up interaction service " + comp);
- } catch (RemoteException e) {
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 30296e1..109d214 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -116,7 +116,7 @@
VoiceInteractionServiceInfo info;
try {
info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
- } catch (RemoteException|PackageManager.NameNotFoundException e) {
+ } catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Voice interaction service not found: " + service, e);
mInfo = null;
mSessionComponentName = null;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8b347cc..b5b4e5f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1051,7 +1051,9 @@
* If there is a ringing incoming call, this method accepts the call on behalf of the user.
* TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use
* this method (clockwork & gearhead).
- *
+ * If the incoming call is a video call, the call will be answered with the same video state as
+ * the incoming call requests. This means, for example, that an incoming call requesting
+ * {@link VideoProfile#STATE_BIDIRECTIONAL} will be answered, accepting that state.
* @hide
*/
@SystemApi
@@ -1066,6 +1068,24 @@
}
/**
+ * If there is a ringing incoming call, this method accepts the call on behalf of the user,
+ * with the specified video state.
+ *
+ * @param videoState The desired video state to answer the call with.
+ * @hide
+ */
+ @SystemApi
+ public void acceptRingingCall(int videoState) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().acceptRingingCallWithVideoState(videoState);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#acceptRingingCallWithVideoState", e);
+ }
+ }
+
+ /**
* Silences the ringer if a ringing call exists.
*
* Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 2e07759..856e210 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -178,6 +178,11 @@
void acceptRingingCall();
/**
+ * @see TelecomServiceImpl#acceptRingingCallWithVideoState(int)
+ */
+ void acceptRingingCallWithVideoState(int videoState);
+
+ /**
* @see TelecomServiceImpl#cancelMissedCallsNotification
*/
void cancelMissedCallsNotification(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index ef39a6c..4785169 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -1012,7 +1012,7 @@
*
* @param tables the new list of enabled single shift tables
*/
- static synchronized void setEnabledSingleShiftTables(int[] tables) {
+ public static synchronized void setEnabledSingleShiftTables(int[] tables) {
sEnabledSingleShiftTables = tables;
sDisableCountryEncodingCheck = true;
@@ -1030,7 +1030,7 @@
*
* @param tables the new list of enabled locking shift tables
*/
- static synchronized void setEnabledLockingShiftTables(int[] tables) {
+ public static synchronized void setEnabledLockingShiftTables(int[] tables) {
sEnabledLockingShiftTables = tables;
sDisableCountryEncodingCheck = true;
}
@@ -1042,7 +1042,7 @@
*
* @return the list of enabled single shift tables
*/
- static synchronized int[] getEnabledSingleShiftTables() {
+ public static synchronized int[] getEnabledSingleShiftTables() {
return sEnabledSingleShiftTables;
}
@@ -1053,7 +1053,7 @@
*
* @return the list of enabled locking shift tables
*/
- static synchronized int[] getEnabledLockingShiftTables() {
+ public static synchronized int[] getEnabledLockingShiftTables() {
return sEnabledLockingShiftTables;
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 572cc6f..a183de5 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -59,6 +59,9 @@
public static final int PHONE_TYPE_SIP = RILConstants.SIP_PHONE;
public static final int PHONE_TYPE_THIRD_PARTY = RILConstants.THIRD_PARTY_PHONE;
public static final int PHONE_TYPE_IMS = RILConstants.IMS_PHONE;
+ // Currently this is used only to differentiate CDMA and CDMALTE Phone in GsmCdma* files. For
+ // anything outside of that, a cdma + lte phone is still CDMA_PHONE
+ public static final int PHONE_TYPE_CDMA_LTE = RILConstants.CDMA_LTE_PHONE;
// Modes for LTE_ON_CDMA
public static final int LTE_ON_CDMA_UNKNOWN = RILConstants.LTE_ON_CDMA_UNKNOWN;
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 7088be8..3c4c04b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -118,6 +118,7 @@
int SIP_PHONE = 3;
int THIRD_PARTY_PHONE = 4;
int IMS_PHONE = 5;
+ int CDMA_LTE_PHONE = 6;
int LTE_ON_CDMA_UNKNOWN = -1;
int LTE_ON_CDMA_FALSE = 0;
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index b12795c..68bde35 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := core-libart core-junit framework
+LOCAL_JAVA_LIBRARIES := core-oj core-libart core-junit framework
LOCAL_STATIC_JAVA_LIBRARIES := junit-runner
LOCAL_MODULE:= android.test.runner
diff --git a/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
new file mode 100644
index 0000000..81648d9
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIJAP/YiWztz/J7MA0GCSqGSIb3DQEBCwUAMCcxFjAUBgNV
+BAMMDVRlc3QgZGVidWcgQ0ExDTALBgNVBAoMBEFPU1AwHhcNMTUxMTA5MjEyNjQ2
+WhcNMTgwODI5MjEyNjQ2WjAnMRYwFAYDVQQDDA1UZXN0IGRlYnVnIENBMQ0wCwYD
+VQQKDARBT1NQMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPFmkOJj
+ehjfvdDr2qTcBWNqNATrW1SuM88Vj00ubUFQ4tZElozj8YnQOw1FeC79c1k88b8R
+6jcqYYp/mw2JYoD6yWcFPHo5BplIpk0EhIUARH/aeoclHvsUN2GGDyTO0vf0CfJn
+9Wp6lSLjyq7V/6tYdk+0cL632t56MHp8TCO+AaveYP1T8JZqx0/50xNcsK7lIqNa
+ctWyRGFxR4ifdVsgkw9WhAB/Ow2uOwN9uLGqzsCd+yXW2weX52EIivoTGZfJo+U8
+Fi0ygnCHBv2jsJA7yWLhHmZ4ijsVtfutIKmN0w+DHkl6S25girXhy0zJp/1QvHGm
+jaF60V1gw471jQIDAQABo1AwTjAdBgNVHQ4EFgQUoq66jncy83L5eeyW1g78s/uq
+iyQwHwYDVR0jBBgwFoAUoq66jncy83L5eeyW1g78s/uqiyQwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAohytuH4CdX0gO8EGVDRVurRH7LO69lwd/6Iw
+hJ1lIK/mzj5RM2itVGTkintyHCLu5giVkHn4FHg4X9qzZaTPOcXv9ntQNS2nacZe
+bY8nfhsAhstJT4nIOWHE3FrZkMDOK6nZHIzfscX3V/VVq5MeA+WzXwmKp6MBNr+E
+oUegXCGjd26Bl6SFz3rD7Qh+dzSTtyf/ECzXaMjpZu3k6fb4EgRz6vdBCHKKtpv6
+Mxcr0nLwdI6LnAGXvJLV4sj+l6Ngg00EeyorG8ATgtmsUrXXOR1e+yDCQv6fjQfs
+CWYztECAUE9hfCXJwb0TBrq9YeJAvcO7iE6S0Pq+X3xNtetE1A==
+-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
new file mode 100644
index 0000000..8da9317
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
new file mode 100644
index 0000000..24eed7a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config>
+ <domain>android.com</domain>
+ <trust-anchors>
+ <certificates src="@raw/ca_certs_pem" />
+ </trust-anchors>
+ </domain-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="@raw/test_debug_ca" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
new file mode 100644
index 0000000..ce0cbc8
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="@raw/test_debug_ca" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
index 43c0e57..f7590fd 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
@@ -22,6 +22,7 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
import junit.framework.Assert;
@@ -69,8 +70,11 @@
public static SSLContext getSSLContext(ConfigSource source) throws Exception {
ApplicationConfig config = new ApplicationConfig(source);
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance("PKIX", new NetworkSecurityConfigProvider());
+ tmf.init(new RootTrustManagerFactorySpi.ApplicationConfigParameters(config));
SSLContext context = SSLContext.getInstance("TLS");
- context.init(null, new TrustManager[] {config.getTrustManager()}, null);
+ context.init(null, tmf.getTrustManagers(), null);
return context;
}
}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index f52a279..c6f3680 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -24,15 +24,23 @@
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Set;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
public class XmlConfigTests extends AndroidTestCase {
+ private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
+
public void testEmptyConfigFile() throws Exception {
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config);
ApplicationConfig appConfig = new ApplicationConfig(source);
@@ -274,6 +282,68 @@
assertFalse(child.isCleartextTrafficPermitted());
}
+ public void testDebugOverridesDisabled() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ MoreAsserts.assertEmpty(anchors);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionFails(context, "android.com", 443);
+ TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+ }
+
+ public void testBasicDebugOverrides() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ MoreAsserts.assertNotEmpty(anchors);
+ for (TrustAnchor anchor : anchors) {
+ assertTrue(anchor.overridesPins);
+ }
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testDebugOverridesWithDomain() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ boolean foundDebugCA = false;
+ for (TrustAnchor anchor : anchors) {
+ if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+ foundDebugCA = true;
+ assertTrue(anchor.overridesPins);
+ }
+ }
+ assertTrue(foundDebugCA);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testDebugInherit() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+ Set<TrustAnchor> anchors = config.getTrustAnchors();
+ boolean foundDebugCA = false;
+ for (TrustAnchor anchor : anchors) {
+ if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+ foundDebugCA = true;
+ assertTrue(anchor.overridesPins);
+ }
+ }
+ assertTrue(foundDebugCA);
+ assertTrue(anchors.size() > 1);
+ SSLContext context = TestUtils.getSSLContext(source);
+ TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+ TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
private void testBadConfig(int configId) throws Exception {
try {
XmlConfigSource source = new XmlConfigSource(getContext(), configId);
@@ -310,4 +380,26 @@
public void testBadConfig5() throws Exception {
testBadConfig(R.xml.bad_config4);
}
+
+ public void testTrustManagerKeystore() throws Exception {
+ XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ Provider provider = new NetworkSecurityConfigProvider();
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance("PKIX", provider);
+ KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keystore.load(null);
+ int i = 0;
+ for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) {
+ keystore.setEntry(String.valueOf(i),
+ new KeyStore.TrustedCertificateEntry(cert),
+ null);
+ i++;
+ }
+ tmf.init(keystore);
+ TrustManager[] tms = tmf.getTrustManagers();
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tms, null);
+ TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
+ }
}
diff --git a/tests/RenderScriptTests/Fountain/Android.mk b/tests/RenderScriptTests/Fountain/Android.mk
deleted file mode 100644
index 0517aef..0000000
--- a/tests/RenderScriptTests/Fountain/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_SDK_VERSION := 17
-
-LOCAL_PACKAGE_NAME := RsFountain
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Fountain/AndroidManifest.xml b/tests/RenderScriptTests/Fountain/AndroidManifest.xml
deleted file mode 100644
index d19b8c3..0000000
--- a/tests/RenderScriptTests/Fountain/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.android.rs.fountain">
- <uses-sdk android:minSdkVersion="14" />
- <application
- android:label="RsFountain"
- android:hardwareAccelerated="true"
- android:icon="@drawable/test_pattern">
- <activity android:name="Fountain">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/RenderScriptTests/Fountain/_index.html b/tests/RenderScriptTests/Fountain/_index.html
deleted file mode 100644
index 223242f..0000000
--- a/tests/RenderScriptTests/Fountain/_index.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<p>An example that renders many dots on the screen that follow a user's touch. The dots fall
-to the bottom of the screen when the user releases the finger.</p>
-
-
-
diff --git a/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png b/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/Fountain/res/drawable/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java
deleted file mode 100644
index 311455a..0000000
--- a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/Fountain.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountain;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-
-import java.lang.Runtime;
-
-public class Fountain extends Activity {
- //EventListener mListener = new EventListener();
-
- private static final String LOG_TAG = "libRS_jni";
- private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = false;
-
- private FountainView mView;
-
- // get the current looper (from your Activity UI thread for instance
-
-
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Create our Preview view and set it as the content of our
- // Activity
- mView = new FountainView(this);
- setContentView(mView);
- }
-
- @Override
- protected void onResume() {
- Log.e("rs", "onResume");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onResume();
- mView.resume();
- }
-
- @Override
- protected void onPause() {
- Log.e("rs", "onPause");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onPause();
- mView.pause();
-
-
-
- //Runtime.getRuntime().exit(0);
- }
-
-
- static void log(String message) {
- if (LOG_ENABLED) {
- Log.v(LOG_TAG, message);
- }
- }
-
-
-}
-
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java
deleted file mode 100644
index 646c807..0000000
--- a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainRS.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountain;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-
-public class FountainRS {
- public static final int PART_COUNT = 50000;
-
- public FountainRS() {
- }
-
- private Resources mRes;
- private RenderScriptGL mRS;
- private ScriptC_fountain mScript;
- public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- rs.bindProgramFragment(pfb.create());
-
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);//
- // Allocation.USAGE_GRAPHICS_VERTEX);
-
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
- mRS.bindRootScript(mScript);
- }
-
- boolean holdingColor[] = new boolean[10];
- public void newTouchPosition(float x, float y, float pressure, int id) {
- if (id >= holdingColor.length) {
- return;
- }
- int rate = (int)(pressure * pressure * 500.f);
- if (rate > 500) {
- rate = 500;
- }
- if (rate > 0) {
- mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
- holdingColor[id] = true;
- } else {
- holdingColor[id] = false;
- }
-
- }
-}
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
deleted file mode 100644
index 98cec55..0000000
--- a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/FountainView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountain;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-public class FountainView extends RSSurfaceView {
-
- public FountainView(Context context) {
- super(context);
- //setFocusable(true);
- }
-
- private RenderScriptGL mRS;
- private FountainRS mRender;
-
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- super.surfaceChanged(holder, format, w, h);
- if (mRS == null) {
- RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
- mRS = createRenderScriptGL(sc);
- mRS.setSurface(holder, w, h);
- mRender = new FountainRS();
- mRender.init(mRS, getResources());
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (mRS != null) {
- mRS = null;
- destroyRenderScriptGL();
- }
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- int act = ev.getActionMasked();
- if (act == ev.ACTION_UP) {
- mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
- return false;
- } else if (act == MotionEvent.ACTION_POINTER_UP) {
- // only one pointer going up, we can get the index like this
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- mRender.newTouchPosition(0, 0, 0, pointerId);
- }
- int count = ev.getHistorySize();
- int pcount = ev.getPointerCount();
-
- for (int p=0; p < pcount; p++) {
- int id = ev.getPointerId(p);
- mRender.newTouchPosition(ev.getX(p),
- ev.getY(p),
- ev.getPressure(p),
- id);
-
- for (int i=0; i < count; i++) {
- mRender.newTouchPosition(ev.getHistoricalX(p, i),
- ev.getHistoricalY(p, i),
- ev.getHistoricalPressure(p, i),
- id);
- }
- }
- return true;
- }
-}
-
-
diff --git a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs b/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs
deleted file mode 100644
index 151b689..0000000
--- a/tests/RenderScriptTests/Fountain/src/com/example/android/rs/fountain/fountain.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Fountain test script
-#pragma version(1)
-#pragma rs_fp_relaxed
-
-#pragma rs java_package_name(com.example.android.rs.fountain)
-
-#pragma stateFragment(parent)
-
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-rs_mesh partMesh;
-
-typedef struct __attribute__((packed, aligned(4))) Point {
- float2 delta;
- float2 position;
- uchar4 color;
-} Point_t;
-Point_t *point;
-
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
-
- rsgDrawMesh(partMesh);
- return 1;
-}
-
-static float4 partColor[10];
-void addParticles(int rate, float x, float y, int index, bool newColor)
-{
- if (newColor) {
- partColor[index].x = rsRand(0.5f, 1.0f);
- partColor[index].y = rsRand(1.0f);
- partColor[index].z = rsRand(1.0f);
- }
- float rMax = ((float)rate) * 0.02f;
- int size = rsAllocationGetDimX(rsGetAllocation(point));
- uchar4 c = rsPackColorTo8888(partColor[index]);
-
- Point_t * np = &point[newPart];
- float2 p = {x, y};
- while (rate--) {
- float angle = rsRand(3.14f * 2.f);
- float len = rsRand(rMax);
- np->delta.x = len * sin(angle);
- np->delta.y = len * cos(angle);
- np->position = p;
- np->color = c;
- newPart++;
- np++;
- if (newPart >= size) {
- newPart = 0;
- np = &point[newPart];
- }
- }
-}
-
diff --git a/tests/RenderScriptTests/FountainFbo/Android.mk b/tests/RenderScriptTests/FountainFbo/Android.mk
deleted file mode 100644
index c0f3323..0000000
--- a/tests/RenderScriptTests/FountainFbo/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-# TODO: build fails with this set
-# LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := RsFountainFbo
-LOCAL_SDK_VERSION := 14
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml b/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml
deleted file mode 100644
index 082744b..0000000
--- a/tests/RenderScriptTests/FountainFbo/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.android.rs.fountainfbo">
- <uses-sdk android:minSdkVersion="14" />
- <application
- android:label="RsFountainFbo"
- android:hardwareAccelerated="true"
- android:icon="@drawable/test_pattern">
- <activity android:name="FountainFbo">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/RenderScriptTests/FountainFbo/_index.html b/tests/RenderScriptTests/FountainFbo/_index.html
deleted file mode 100644
index 5508657..0000000
--- a/tests/RenderScriptTests/FountainFbo/_index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<p>An example that renders many dots on the screen that follow a user's touch. The dots fall
-to the bottom of the screen when no touch is detected. This example modifies
-the <a href="../Fountain/index.html">Fountain</a> sample to include rendering to a
-a framebuffer object as well as the default framebuffer.</p>
-
-
-
diff --git a/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png b/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/FountainFbo/res/drawable/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java
deleted file mode 100644
index d8ba30f..0000000
--- a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFbo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountainfbo;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class FountainFbo extends Activity {
- private static final String LOG_TAG = "libRS_jni";
- private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = false;
-
- private FountainFboView mView;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- /* Create our Preview view and set it as the content of our Activity */
- mView = new FountainFboView(this);
- setContentView(mView);
- }
-
- @Override
- protected void onResume() {
- Log.e("rs", "onResume");
-
- /* Ideally a game should implement onResume() and onPause()
- to take appropriate action when the activity loses focus */
- super.onResume();
- mView.resume();
- }
-
- @Override
- protected void onPause() {
- Log.e("rs", "onPause");
-
- /* Ideally a game should implement onResume() and onPause()
- to take appropriate action when the activity loses focus */
- super.onPause();
- mView.pause();
- }
-
- static void log(String message) {
- if (LOG_ENABLED) {
- Log.v(LOG_TAG, message);
- }
- }
-}
-
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java
deleted file mode 100644
index 3bf3ff1..0000000
--- a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboRS.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountainfbo;
-
-import android.content.res.Resources;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Mesh;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramFragmentFixedFunction;
-import android.renderscript.RenderScriptGL;
-import android.renderscript.Type;
-
-public class FountainFboRS {
- public static final int PART_COUNT = 50000;
-
- public FountainFboRS() {
- }
-
- private Resources mRes;
- private RenderScriptGL mRS;
- private ScriptC_fountainfbo mScript;
- private Allocation mColorBuffer;
- private ProgramFragment mProgramFragment;
- private ProgramFragment mTextureProgramFragment;
- public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
-
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- mScript = new ScriptC_fountainfbo(mRS, mRes, R.raw.fountainfbo);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
-
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- mProgramFragment = pfb.create();
- mScript.set_gProgramFragment(mProgramFragment);
-
- /* Second fragment shader to use a texture (framebuffer object) to draw with */
- pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
- ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-
- /* Set the fragment shader in the Renderscript runtime */
- mTextureProgramFragment = pfb.create();
- mScript.set_gTextureProgramFragment(mTextureProgramFragment);
-
- /* Create the allocation for the color buffer */
- Type.Builder colorBuilder = new Type.Builder(mRS, Element.RGBA_8888(mRS));
- colorBuilder.setX(256).setY(256);
- mColorBuffer = Allocation.createTyped(mRS, colorBuilder.create(),
- Allocation.USAGE_GRAPHICS_TEXTURE |
- Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-
- /* Set the allocation in the Renderscript runtime */
- mScript.set_gColorBuffer(mColorBuffer);
-
- mRS.bindRootScript(mScript);
- }
-
- boolean holdingColor[] = new boolean[10];
- public void newTouchPosition(float x, float y, float pressure, int id) {
- if (id >= holdingColor.length) {
- return;
- }
- int rate = (int)(pressure * pressure * 500.f);
- if (rate > 500) {
- rate = 500;
- }
- if (rate > 0) {
- mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
- holdingColor[id] = true;
- } else {
- holdingColor[id] = false;
- }
-
- }
-}
-
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
deleted file mode 100644
index 8636717..0000000
--- a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/FountainFboView.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.example.android.rs.fountainfbo;
-
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-import android.content.Context;
-import android.view.SurfaceHolder;
-import android.view.MotionEvent;
-
-public class FountainFboView extends RSSurfaceView {
-
- public FountainFboView(Context context) {
- super(context);
- }
-
- private RenderScriptGL mRS;
- private FountainFboRS mRender;
-
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- super.surfaceChanged(holder, format, w, h);
- if (mRS == null) {
- RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
- mRS = createRenderScriptGL(sc);
- mRS.setSurface(holder, w, h);
- mRender = new FountainFboRS();
- mRender.init(mRS, getResources());
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- android.util.Log.e("rs", "onDetachedFromWindow");
- if (mRS != null) {
- mRS = null;
- destroyRenderScriptGL();
- }
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- int act = ev.getActionMasked();
- if (act == ev.ACTION_UP) {
- mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
- return false;
- } else if (act == MotionEvent.ACTION_POINTER_UP) {
- // only one pointer going up, we can get the index like this
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- mRender.newTouchPosition(0, 0, 0, pointerId);
- }
- int count = ev.getHistorySize();
- int pcount = ev.getPointerCount();
-
- for (int p=0; p < pcount; p++) {
- int id = ev.getPointerId(p);
- mRender.newTouchPosition(ev.getX(p),
- ev.getY(p),
- ev.getPressure(p),
- id);
-
- for (int i=0; i < count; i++) {
- mRender.newTouchPosition(ev.getHistoricalX(p, i),
- ev.getHistoricalY(p, i),
- ev.getHistoricalPressure(p, i),
- id);
- }
- }
- return true;
- }
-}
-
-
diff --git a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs b/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs
deleted file mode 100644
index 763f6ba..0000000
--- a/tests/RenderScriptTests/FountainFbo/src/com/example/android/rs/fountainfbo/fountainfbo.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-// Fountain test script
-#pragma version(1)
-
-#pragma rs java_package_name(com.example.android.rs.fountainfbo)
-
-#pragma stateFragment(parent)
-
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-rs_mesh partMesh;
-rs_program_vertex gProgramVertex;
-
-//allocation for color buffer
-rs_allocation gColorBuffer;
-//fragment shader for rendering without a texture (used for rendering to framebuffer object)
-rs_program_fragment gProgramFragment;
-//fragment shader for rendering with a texture (used for rendering to default framebuffer)
-rs_program_fragment gTextureProgramFragment;
-
-typedef struct __attribute__((packed, aligned(4))) Point {
- float2 delta;
- float2 position;
- uchar4 color;
-} Point_t;
-Point_t *point;
-
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
- //Tell Renderscript runtime to render to the frame buffer object
- rsgBindColorTarget(gColorBuffer, 0);
-
- //Begin rendering on a white background
- rsgClearColor(1.f, 1.f, 1.f, 1.f);
- rsgDrawMesh(partMesh);
-
- //When done, tell Renderscript runtime to stop rendering to framebuffer object
- rsgClearAllRenderTargets();
-
- //Bind a new fragment shader that declares the framebuffer object to be used as a texture
- rsgBindProgramFragment(gTextureProgramFragment);
-
- //Bind the framebuffer object to the fragment shader at slot 0 as a texture
- rsgBindTexture(gTextureProgramFragment, 0, gColorBuffer);
-
- //Draw a quad using the framebuffer object as the texture
- float startX = 10, startY = 10;
- float s = 256;
- rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
- startX, startY + s, 0, 0, 0,
- startX + s, startY + s, 0, 1, 0,
- startX + s, startY, 0, 1, 1);
-
- //Rebind the original fragment shader to render as normal
- rsgBindProgramFragment(gProgramFragment);
-
- //Render the main scene
- rsgDrawMesh(partMesh);
-
- return 1;
-}
-
-static float4 partColor[10];
-void addParticles(int rate, float x, float y, int index, bool newColor)
-{
- if (newColor) {
- partColor[index].x = rsRand(0.5f, 1.0f);
- partColor[index].y = rsRand(1.0f);
- partColor[index].z = rsRand(1.0f);
- }
- float rMax = ((float)rate) * 0.02f;
- int size = rsAllocationGetDimX(rsGetAllocation(point));
- uchar4 c = rsPackColorTo8888(partColor[index]);
-
- Point_t * np = &point[newPart];
- float2 p = {x, y};
- while (rate--) {
- float angle = rsRand(3.14f * 2.f);
- float len = rsRand(rMax);
- np->delta.x = len * sin(angle);
- np->delta.y = len * cos(angle);
- np->position = p;
- np->color = c;
- newPart++;
- np++;
- if (newPart >= size) {
- newPart = 0;
- np = &point[newPart];
- }
- }
-}
-
-
diff --git a/tests/RenderScriptTests/Fountain_v11/Android.mk b/tests/RenderScriptTests/Fountain_v11/Android.mk
deleted file mode 100644
index ac2690c..0000000
--- a/tests/RenderScriptTests/Fountain_v11/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
-
-LOCAL_PACKAGE_NAME := Fountain_v11
-LOCAL_SDK_VERSION := 11
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Fountain_v11/AndroidManifest.xml b/tests/RenderScriptTests/Fountain_v11/AndroidManifest.xml
deleted file mode 100644
index fcb4faf..0000000
--- a/tests/RenderScriptTests/Fountain_v11/AndroidManifest.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.fountain_v11">
- <uses-sdk android:minSdkVersion="11" />
- <application
- android:label="Fountain_v11"
- android:icon="@drawable/test_pattern">
- <activity android:name="Fountain_v11">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/RenderScriptTests/Fountain_v11/_index.html b/tests/RenderScriptTests/Fountain_v11/_index.html
deleted file mode 100644
index 223242f..0000000
--- a/tests/RenderScriptTests/Fountain_v11/_index.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<p>An example that renders many dots on the screen that follow a user's touch. The dots fall
-to the bottom of the screen when the user releases the finger.</p>
-
-
-
diff --git a/tests/RenderScriptTests/Fountain_v11/res/drawable/test_pattern.png b/tests/RenderScriptTests/Fountain_v11/res/drawable/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/Fountain_v11/res/drawable/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainRS.java b/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainRS.java
deleted file mode 100644
index e858100..0000000
--- a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainRS.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.fountain_v11;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-
-public class FountainRS {
- public static final int PART_COUNT = 50000;
-
- public FountainRS() {
- }
-
- private Resources mRes;
- private RenderScriptGL mRS;
- private ScriptC_fountain mScript;
- public void init(RenderScriptGL rs, Resources res, int width, int height) {
- mRS = rs;
- mRes = res;
-
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- rs.bindProgramFragment(pfb.create());
-
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);//
- // Allocation.USAGE_GRAPHICS_VERTEX);
-
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
- mRS.bindRootScript(mScript);
- }
-
- boolean holdingColor[] = new boolean[10];
- public void newTouchPosition(float x, float y, float pressure, int id) {
- if (id >= holdingColor.length) {
- return;
- }
- int rate = (int)(pressure * pressure * 500.f);
- if (rate > 500) {
- rate = 500;
- }
- if (rate > 0) {
- mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
- holdingColor[id] = true;
- } else {
- holdingColor[id] = false;
- }
-
- }
-}
diff --git a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainView.java b/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainView.java
deleted file mode 100644
index e82376c..0000000
--- a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/FountainView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.fountain_v11;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-public class FountainView extends RSSurfaceView {
-
- public FountainView(Context context) {
- super(context);
- //setFocusable(true);
- }
-
- private RenderScriptGL mRS;
- private FountainRS mRender;
-
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- super.surfaceChanged(holder, format, w, h);
- if (mRS == null) {
- RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
- mRS = createRenderScriptGL(sc);
- mRS.setSurface(holder, w, h);
- mRender = new FountainRS();
- mRender.init(mRS, getResources(), w, h);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- if (mRS != null) {
- mRS = null;
- destroyRenderScriptGL();
- }
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- int act = ev.getActionMasked();
- if (act == ev.ACTION_UP) {
- mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
- return false;
- } else if (act == MotionEvent.ACTION_POINTER_UP) {
- // only one pointer going up, we can get the index like this
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- mRender.newTouchPosition(0, 0, 0, pointerId);
- }
- int count = ev.getHistorySize();
- int pcount = ev.getPointerCount();
-
- for (int p=0; p < pcount; p++) {
- int id = ev.getPointerId(p);
- mRender.newTouchPosition(ev.getX(p),
- ev.getY(p),
- ev.getPressure(p),
- id);
-
- for (int i=0; i < count; i++) {
- mRender.newTouchPosition(ev.getHistoricalX(p, i),
- ev.getHistoricalY(p, i),
- ev.getHistoricalPressure(p, i),
- id);
- }
- }
- return true;
- }
-}
-
-
diff --git a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/Fountain_v11.java b/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/Fountain_v11.java
deleted file mode 100644
index 2c07b27..0000000
--- a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/Fountain_v11.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.fountain_v11;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Config;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-
-import java.lang.Runtime;
-
-public class Fountain_v11 extends Activity {
- //EventListener mListener = new EventListener();
-
- private static final String LOG_TAG = "libRS_jni";
- private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
-
- private FountainView mView;
-
- // get the current looper (from your Activity UI thread for instance
-
-
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Create our Preview view and set it as the content of our
- // Activity
- mView = new FountainView(this);
- setContentView(mView);
- }
-
- @Override
- protected void onResume() {
- Log.e("rs", "onResume");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onResume();
- mView.resume();
- }
-
- @Override
- protected void onPause() {
- Log.e("rs", "onPause");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onPause();
- mView.pause();
-
-
-
- //Runtime.getRuntime().exit(0);
- }
-
-
- static void log(String message) {
- if (LOG_ENABLED) {
- Log.v(LOG_TAG, message);
- }
- }
-
-
-}
-
diff --git a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/fountain.rs b/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/fountain.rs
deleted file mode 100644
index 3b6c89a..0000000
--- a/tests/RenderScriptTests/Fountain_v11/src/com/android/fountain/fountain.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-// Fountain test script
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.fountain_v11)
-
-#pragma stateFragment(parent)
-
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-rs_mesh partMesh;
-
-typedef struct __attribute__((packed, aligned(4))) Point {
- float2 delta;
- float2 position;
- uchar4 color;
-} Point_t;
-Point_t *point;
-
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
-
- rsgDrawMesh(partMesh);
- return 1;
-}
-
-static float4 partColor[10];
-void addParticles(int rate, float x, float y, int index, bool newColor)
-{
- if (newColor) {
- partColor[index].x = rsRand(0.5f, 1.0f);
- partColor[index].y = rsRand(1.0f);
- partColor[index].z = rsRand(1.0f);
- }
- float rMax = ((float)rate) * 0.02f;
- int size = rsAllocationGetDimX(rsGetAllocation(point));
- uchar4 c = rsPackColorTo8888(partColor[index]);
-
- Point_t * np = &point[newPart];
- float2 p = {x, y};
- while (rate--) {
- float angle = rsRand(3.14f * 2.f);
- float len = rsRand(rMax);
- np->delta.x = len * sin(angle);
- np->delta.y = len * cos(angle);
- np->position = p;
- np->color = c;
- newPart++;
- np++;
- if (newPart >= size) {
- newPart = 0;
- np = &point[newPart];
- }
- }
-}
-
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 67b9d77..8eb30d2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -160,6 +160,36 @@
}
},
+ new Test("with topic Hello") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("hihi")
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent2())
+ .setTopic(new Notification.Topic("hello", "Hello"))
+ .build();
+
+ mNM.notify(999, n);
+ }
+ },
+
+ new Test("with topic GoodBye") {
+ public void run() {
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("byebye")
+ .setContentText("This is a notification!!!")
+ .setContentIntent(makeIntent2())
+ .setTopic(new Notification.Topic("bye", "Goodbye"))
+ .build();
+
+ mNM.notify(9999, n);
+ }
+ },
+
new Test("Whens") {
public void run()
{
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index ec29c38..d8e0aac 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -58,8 +58,9 @@
ResourceValues.cpp \
SdkConstants.cpp \
StringPool.cpp \
- XmlDom.cpp \
- XmlPullParser.cpp
+ xml/XmlDom.cpp \
+ xml/XmlPullParser.cpp \
+ xml/XmlUtil.cpp
testSources := \
compile/IdAssigner_test.cpp \
@@ -90,8 +91,9 @@
ResourceUtils_test.cpp \
StringPool_test.cpp \
ValueVisitor_test.cpp \
- XmlDom_test.cpp \
- XmlPullParser_test.cpp
+ xml/XmlDom_test.cpp \
+ xml/XmlPullParser_test.cpp \
+ xml/XmlUtil_test.cpp
toolSources := \
compile/Compile.cpp \
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index 7ea26b3..ab4d284 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -18,7 +18,6 @@
#define AAPT_DIAGNOSTICS_H
#include "Source.h"
-
#include "util/StringPiece.h"
#include "util/Util.h"
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index f2a1878..c2ddb5c 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -19,9 +19,8 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "XmlPullParser.h"
-
#include "util/Util.h"
+#include "xml/XmlPullParser.h"
#include <sstream>
@@ -29,26 +28,6 @@
constexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2";
-static Maybe<StringPiece16> findAttribute(XmlPullParser* parser, const StringPiece16& name) {
- auto iter = parser->findAttribute(u"", name);
- if (iter != parser->endAttributes()) {
- return StringPiece16(util::trimWhitespace(iter->value));
- }
- return {};
-}
-
-static Maybe<StringPiece16> findNonEmptyAttribute(XmlPullParser* parser,
- const StringPiece16& name) {
- auto iter = parser->findAttribute(u"", name);
- if (iter != parser->endAttributes()) {
- StringPiece16 trimmed = util::trimWhitespace(iter->value);
- if (!trimmed.empty()) {
- return trimmed;
- }
- }
- return {};
-}
-
/**
* Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
*/
@@ -65,7 +44,7 @@
/**
* Build a string from XML that converts nested elements into Span objects.
*/
-bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
+bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString,
StyleString* outStyleString) {
std::vector<Span> spanStack;
@@ -74,9 +53,9 @@
outStyleString->spans.clear();
util::StringBuilder builder;
size_t depth = 1;
- while (XmlPullParser::isGoodEvent(parser->next())) {
- const XmlPullParser::Event event = parser->getEvent();
- if (event == XmlPullParser::Event::kEndElement) {
+ while (xml::XmlPullParser::isGoodEvent(parser->next())) {
+ const xml::XmlPullParser::Event event = parser->getEvent();
+ if (event == xml::XmlPullParser::Event::kEndElement) {
if (!parser->getElementNamespace().empty()) {
// We already warned and skipped the start element, so just skip here too
continue;
@@ -91,11 +70,11 @@
outStyleString->spans.push_back(spanStack.back());
spanStack.pop_back();
- } else if (event == XmlPullParser::Event::kText) {
+ } else if (event == xml::XmlPullParser::Event::kText) {
outRawString->append(parser->getText());
builder.append(parser->getText());
- } else if (event == XmlPullParser::Event::kStartElement) {
+ } else if (event == xml::XmlPullParser::Event::kStartElement) {
if (!parser->getElementNamespace().empty()) {
if (parser->getElementNamespace() != sXliffNamespaceUri) {
// Only warn if this isn't an xliff namespace.
@@ -128,7 +107,7 @@
spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
}
- } else if (event == XmlPullParser::Event::kComment) {
+ } else if (event == xml::XmlPullParser::Event::kComment) {
// Skip
} else {
assert(false);
@@ -140,11 +119,11 @@
return !error;
}
-bool ResourceParser::parse(XmlPullParser* parser) {
+bool ResourceParser::parse(xml::XmlPullParser* parser) {
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip comments and text.
continue;
}
@@ -159,7 +138,7 @@
break;
};
- if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< "xml parser error: " << parser->getLastError());
return false;
@@ -167,10 +146,11 @@
return !error;
}
-static bool shouldStripResource(XmlPullParser* parser, const Maybe<std::u16string> productToMatch) {
- assert(parser->getEvent() == XmlPullParser::Event::kStartElement);
+static bool shouldStripResource(const xml::XmlPullParser* parser,
+ const Maybe<std::u16string> productToMatch) {
+ assert(parser->getEvent() == xml::XmlPullParser::Event::kStartElement);
- if (Maybe<StringPiece16> maybeProduct = findNonEmptyAttribute(parser, u"product")) {
+ if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
if (!productToMatch) {
if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
// We didn't specify a product and this is not a default product, so skip.
@@ -229,20 +209,20 @@
return !error;
}
-bool ResourceParser::parseResources(XmlPullParser* parser) {
+bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
std::set<ResourceName> strippedResources;
bool error = false;
std::u16string comment;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- const XmlPullParser::Event event = parser->getEvent();
- if (event == XmlPullParser::Event::kComment) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ const xml::XmlPullParser::Event event = parser->getEvent();
+ if (event == xml::XmlPullParser::Event::kComment) {
comment = parser->getComment();
continue;
}
- if (event == XmlPullParser::Event::kText) {
+ if (event == xml::XmlPullParser::Event::kText) {
if (!util::trimWhitespace(parser->getText()).empty()) {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
<< "plain text not allowed here");
@@ -251,7 +231,7 @@
continue;
}
- assert(event == XmlPullParser::Event::kStartElement);
+ assert(event == xml::XmlPullParser::Event::kStartElement);
if (!parser->getElementNamespace().empty()) {
// Skip unknown namespace.
@@ -266,7 +246,7 @@
if (elementName == u"item") {
// Items simply have their type encoded in the type attribute.
- if (Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type")) {
+ if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) {
elementName = maybeType.value().toString();
} else {
mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
@@ -280,7 +260,7 @@
parsedResource.source = mSource.withLine(parser->getLineNumber());
parsedResource.comment = std::move(comment);
- if (Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name")) {
+ if (Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name")) {
parsedResource.name.entry = maybeName.value().toString();
} else if (elementName != u"public-group") {
@@ -403,7 +383,7 @@
* an Item. If allowRawValue is false, nullptr is returned in this
* case.
*/
-std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint32_t typeMask,
+std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
const bool allowRawValue) {
const size_t beginXmlLine = parser->getLineNumber();
@@ -432,10 +412,7 @@
if (processedItem) {
// Fix up the reference.
if (Reference* ref = valueCast<Reference>(processedItem.get())) {
- if (Maybe<ResourceName> transformedName =
- parser->transformPackage(ref->name.value(), u"")) {
- ref->name = std::move(transformedName);
- }
+ transformReferenceFromNamespace(parser, u"", ref);
}
return processedItem;
}
@@ -456,11 +433,11 @@
return {};
}
-bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
bool formatted = true;
- if (Maybe<StringPiece16> formattedAttr = findAttribute(parser, u"formatted")) {
+ if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) {
if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) {
mDiag->error(DiagMessage(source) << "invalid value for 'formatted'. Must be a boolean");
return false;
@@ -468,7 +445,7 @@
}
bool translateable = mOptions.translatable;
- if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) {
+ if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) {
if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
mDiag->error(DiagMessage(source)
<< "invalid value for 'translatable'. Must be a boolean");
@@ -495,7 +472,7 @@
return true;
}
-bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseColor(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
@@ -506,7 +483,7 @@
return true;
}
-bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
uint32_t typeMask = 0;
@@ -540,10 +517,10 @@
return true;
}
-bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
if (!maybeType) {
mDiag->error(DiagMessage(source) << "<public> must have a 'type' attribute");
return false;
@@ -558,7 +535,7 @@
outResource->name.type = *parsedType;
- if (Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"id")) {
+ if (Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"id")) {
android::Res_value val;
bool result = android::ResTable::stringToInt(maybeId.value().data(),
maybeId.value().size(), &val);
@@ -580,10 +557,10 @@
return true;
}
-bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
if (!maybeType) {
mDiag->error(DiagMessage(source) << "<public-group> must have a 'type' attribute");
return false;
@@ -596,7 +573,7 @@
return false;
}
- Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"first-id");
+ Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id");
if (!maybeId) {
mDiag->error(DiagMessage(source) << "<public-group> must have a 'first-id' attribute");
return false;
@@ -615,11 +592,11 @@
std::u16string comment;
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
comment = util::trimWhitespace(parser->getComment()).toString();
continue;
- } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip text.
continue;
}
@@ -628,20 +605,20 @@
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
if (elementNamespace.empty() && elementName == u"public") {
- Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
if (!maybeName) {
mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute");
error = true;
continue;
}
- if (findNonEmptyAttribute(parser, u"id")) {
+ if (xml::findNonEmptyAttribute(parser, u"id")) {
mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
error = true;
continue;
}
- if (findNonEmptyAttribute(parser, u"type")) {
+ if (xml::findNonEmptyAttribute(parser, u"type")) {
mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
error = true;
continue;
@@ -666,10 +643,10 @@
return !error;
}
-bool ResourceParser::parseSymbol(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type");
+ Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
if (!maybeType) {
mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a "
"'type' attribute");
@@ -715,17 +692,16 @@
return mask;
}
-
-
-bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
outResource->source = mSource.withLine(parser->getLineNumber());
return parseAttrImpl(parser, outResource, false);
}
-bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) {
+bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+ bool weak) {
uint32_t typeMask = 0;
- Maybe<StringPiece16> maybeFormat = findAttribute(parser, u"format");
+ Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
if (maybeFormat) {
typeMask = parseFormatAttribute(maybeFormat.value());
if (typeMask == 0) {
@@ -735,28 +711,62 @@
}
}
- // If this is a declaration, the package name may be in the name. Separate these out.
- // Eg. <attr name="android:text" />
- // No format attribute is allowed.
- if (weak && !maybeFormat) {
- StringPiece16 package, type, name;
- ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name);
- if (type.empty() && !package.empty()) {
- outResource->name.package = package.toString();
- outResource->name.entry = name.toString();
+ Maybe<int32_t> maybeMin, maybeMax;
+
+ if (Maybe<StringPiece16> maybeMinStr = xml::findAttribute(parser, u"min")) {
+ StringPiece16 minStr = util::trimWhitespace(maybeMinStr.value());
+ if (!minStr.empty()) {
+ android::Res_value value;
+ if (android::ResTable::stringToInt(minStr.data(), minStr.size(), &value)) {
+ maybeMin = static_cast<int32_t>(value.data);
+ }
+ }
+
+ if (!maybeMin) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid 'min' value '" << minStr << "'");
+ return false;
}
}
- std::vector<Attribute::Symbol> items;
+ if (Maybe<StringPiece16> maybeMaxStr = xml::findAttribute(parser, u"max")) {
+ StringPiece16 maxStr = util::trimWhitespace(maybeMaxStr.value());
+ if (!maxStr.empty()) {
+ android::Res_value value;
+ if (android::ResTable::stringToInt(maxStr.data(), maxStr.size(), &value)) {
+ maybeMax = static_cast<int32_t>(value.data);
+ }
+ }
+
+ if (!maybeMax) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid 'max' value '" << maxStr << "'");
+ return false;
+ }
+ }
+
+ if ((maybeMin || maybeMax) && (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "'min' and 'max' can only be used when format='integer'");
+ return false;
+ }
+
+ struct SymbolComparator {
+ bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
+ return a.symbol.name.value() < b.symbol.name.value();
+ }
+ };
+
+ std::set<Attribute::Symbol, SymbolComparator> items;
std::u16string comment;
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
comment = util::trimWhitespace(parser->getComment()).toString();
continue;
- } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip text.
continue;
}
@@ -785,15 +795,27 @@
}
if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
+ Attribute::Symbol& symbol = s.value();
ParsedResource childResource;
- childResource.name = s.value().symbol.name.value();
+ childResource.name = symbol.symbol.name.value();
childResource.source = itemSource;
childResource.value = util::make_unique<Id>();
outResource->childResources.push_back(std::move(childResource));
- s.value().symbol.setComment(std::move(comment));
- s.value().symbol.setSource(itemSource);
- items.push_back(std::move(s.value()));
+ symbol.symbol.setComment(std::move(comment));
+ symbol.symbol.setSource(itemSource);
+
+ auto insertResult = items.insert(std::move(symbol));
+ if (!insertResult.second) {
+ const Attribute::Symbol& existingSymbol = *insertResult.first;
+ mDiag->error(DiagMessage(itemSource)
+ << "duplicate symbol '" << existingSymbol.symbol.name.value().entry
+ << "'");
+
+ mDiag->note(DiagMessage(existingSymbol.symbol.getSource())
+ << "first defined here");
+ error = true;
+ }
} else {
error = true;
}
@@ -810,23 +832,30 @@
}
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
- attr->symbols.swap(items);
+ attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
+ if (maybeMin) {
+ attr->minInt = maybeMin.value();
+ }
+
+ if (maybeMax) {
+ attr->maxInt = maybeMax.value();
+ }
outResource->value = std::move(attr);
return true;
}
-Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser,
+Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
const StringPiece16& tag) {
const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
if (!maybeName) {
mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
return {};
}
- Maybe<StringPiece16> maybeValue = findNonEmptyAttribute(parser, u"value");
+ Maybe<StringPiece16> maybeValue = xml::findNonEmptyAttribute(parser, u"value");
if (!maybeValue) {
mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
return {};
@@ -845,12 +874,19 @@
val.data };
}
-static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) {
+static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
str = util::trimWhitespace(str);
- const char16_t* const start = str.data();
+ const char16_t* start = str.data();
const char16_t* const end = start + str.size();
const char16_t* p = start;
+ Reference ref;
+ if (p != end && *p == u'*') {
+ ref.privateReference = true;
+ start++;
+ p++;
+ }
+
StringPiece16 package;
StringPiece16 name;
while (p != end) {
@@ -862,28 +898,28 @@
p++;
}
- return ResourceName(package.toString(), ResourceType::kAttr,
+ ref.name = ResourceName(package.toString(), ResourceType::kAttr,
name.empty() ? str.toString() : name.toString());
+ return Maybe<Reference>(std::move(ref));
}
-bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
+bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) {
const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name");
+ Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
if (!maybeName) {
mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
return false;
}
- Maybe<ResourceName> maybeKey = parseXmlAttributeName(maybeName.value());
+ Maybe<Reference> maybeKey = parseXmlAttributeName(maybeName.value());
if (!maybeKey) {
mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
return false;
}
- if (Maybe<ResourceName> transformedName = parser->transformPackage(maybeKey.value(), u"")) {
- maybeKey = std::move(transformedName);
- }
+ transformReferenceFromNamespace(parser, u"", &maybeKey.value());
+ maybeKey.value().setSource(source);
std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
if (!value) {
@@ -891,15 +927,15 @@
return false;
}
- style->entries.push_back(Style::Entry{ Reference(maybeKey.value()), std::move(value) });
+ style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
return true;
}
-bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Style> style = util::make_unique<Style>();
- Maybe<StringPiece16> maybeParent = findAttribute(parser, u"parent");
+ Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent");
if (maybeParent) {
// If the parent is empty, we don't have a parent, but we also don't infer either.
if (!maybeParent.value().empty()) {
@@ -910,10 +946,9 @@
return false;
}
- if (Maybe<ResourceName> transformedName =
- parser->transformPackage(style->parent.value().name.value(), u"")) {
- style->parent.value().name = std::move(transformedName);
- }
+ // Transform the namespace prefix to the actual package name, and mark the reference as
+ // private if appropriate.
+ transformReferenceFromNamespace(parser, u"", &style->parent.value());
}
} else {
@@ -922,15 +957,15 @@
size_t pos = styleName.find_last_of(u'.');
if (pos != std::string::npos) {
style->parentInferred = true;
- style->parent = Reference(
- ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos)));
+ style->parent = Reference(ResourceName({}, ResourceType::kStyle,
+ styleName.substr(0, pos)));
}
}
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip text and comments.
continue;
}
@@ -955,15 +990,15 @@
return true;
}
-bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResource,
+bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource,
uint32_t typeMask) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Array> array = util::make_unique<Array>();
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip text and comments.
continue;
}
@@ -996,14 +1031,14 @@
return true;
}
-bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Skip text and comments.
continue;
}
@@ -1012,16 +1047,15 @@
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
if (elementNamespace.empty() && elementName == u"item") {
- const auto endAttrIter = parser->endAttributes();
- auto attrIter = parser->findAttribute(u"", u"quantity");
- if (attrIter == endAttrIter || attrIter->value.empty()) {
+ Maybe<StringPiece16> maybeQuantity = xml::findNonEmptyAttribute(parser, u"quantity");
+ if (!maybeQuantity) {
mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
<< "'quantity'");
error = true;
continue;
}
- StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
+ StringPiece16 trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
size_t index = 0;
if (trimmedQuantity == u"zero") {
index = Plural::Zero;
@@ -1071,7 +1105,7 @@
return true;
}
-bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
@@ -1081,11 +1115,11 @@
std::u16string comment;
bool error = false;
const size_t depth = parser->getDepth();
- while (XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == XmlPullParser::Event::kComment) {
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
comment = util::trimWhitespace(parser->getComment()).toString();
continue;
- } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
// Ignore text.
continue;
}
@@ -1094,17 +1128,29 @@
const std::u16string& elementNamespace = parser->getElementNamespace();
const std::u16string& elementName = parser->getElementName();
if (elementNamespace.empty() && elementName == u"attr") {
- const auto endAttrIter = parser->endAttributes();
- auto attrIter = parser->findAttribute(u"", u"name");
- if (attrIter == endAttrIter || attrIter->value.empty()) {
+ Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
+ if (!maybeName) {
mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
error = true;
continue;
}
+ // If this is a declaration, the package name may be in the name. Separate these out.
+ // Eg. <attr name="android:text" />
+ Maybe<Reference> maybeRef = parseXmlAttributeName(maybeName.value());
+ if (!maybeRef) {
+ mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
+ << maybeName.value() << "'");
+ error = true;
+ continue;
+ }
+
+ Reference& childRef = maybeRef.value();
+ xml::transformReferenceFromNamespace(parser, u"", &childRef);
+
// Create the ParsedResource that will add the attribute to the table.
ParsedResource childResource;
- childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
+ childResource.name = childRef.name.value();
childResource.source = itemSource;
childResource.comment = std::move(comment);
@@ -1114,7 +1160,6 @@
}
// Create the reference to this attribute.
- Reference childRef(childResource.name);
childRef.setComment(childResource.comment);
childRef.setSource(itemSource);
styleable->entries.push_back(std::move(childRef));
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 18101ee..1150758 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -22,10 +22,9 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "StringPool.h"
-#include "XmlPullParser.h"
-
#include "util/Maybe.h"
#include "util/StringPiece.h"
+#include "xml/XmlPullParser.h"
#include <memory>
@@ -57,7 +56,7 @@
ResourceParser(const ResourceParser&) = delete; // No copy.
- bool parse(XmlPullParser* parser);
+ bool parse(xml::XmlPullParser* parser);
private:
/*
@@ -66,7 +65,7 @@
* contains the escaped and whitespace trimmed text, while `outRawString`
* contains the unescaped text. Returns true on success.
*/
- bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
+ bool flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString,
StyleString* outStyleString);
/*
@@ -75,24 +74,25 @@
* If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a
* RawString is returned. Otherwise this returns false;
*/
- std::unique_ptr<Item> parseXml(XmlPullParser* parser, const uint32_t typeMask,
+ std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
const bool allowRawValue);
- bool parseResources(XmlPullParser* parser);
- bool parseString(XmlPullParser* parser, ParsedResource* outResource);
- bool parseColor(XmlPullParser* parser, ParsedResource* outResource);
- bool parsePrimitive(XmlPullParser* parser, ParsedResource* outResource);
- bool parsePublic(XmlPullParser* parser, ParsedResource* outResource);
- bool parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource);
- bool parseSymbol(XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttr(XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak);
- Maybe<Attribute::Symbol> parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag);
- bool parseStyle(XmlPullParser* parser, ParsedResource* outResource);
- bool parseStyleItem(XmlPullParser* parser, Style* style);
- bool parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource);
- bool parseArray(XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
- bool parsePlural(XmlPullParser* parser, ParsedResource* outResource);
+ bool parseResources(xml::XmlPullParser* parser);
+ bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseColor(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
+ Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const StringPiece16& tag);
+ bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
+ bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
+ bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
IDiagnostics* mDiag;
ResourceTable* mTable;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index b59eb95..6e0812b 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -18,9 +18,8 @@
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "XmlPullParser.h"
-
#include "test/Context.h"
+#include "xml/XmlPullParser.h"
#include <gtest/gtest.h>
#include <sstream>
@@ -36,7 +35,7 @@
input << "<attr name=\"foo\"/>" << std::endl;
ResourceTable table;
ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
- XmlPullParser xmlParser(input);
+ xml::XmlPullParser xmlParser(input);
ASSERT_FALSE(parser.parse(&xmlParser));
}
@@ -56,7 +55,7 @@
parserOptions.product = product;
ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
parserOptions);
- XmlPullParser xmlParser(input);
+ xml::XmlPullParser xmlParser(input);
if (parser.parse(&xmlParser)) {
return ::testing::AssertionSuccess();
}
@@ -139,6 +138,22 @@
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}
+TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
+ std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
+ ASSERT_TRUE(testParse(input));
+
+ Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask);
+ EXPECT_EQ(10, attr->minInt);
+ EXPECT_EQ(23, attr->maxInt);
+}
+
+TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
+ std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
+ ASSERT_FALSE(testParse(input));
+}
+
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
std::string input = "<declare-styleable name=\"Styleable\">\n"
" <attr name=\"foo\" />\n"
@@ -360,6 +375,25 @@
EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value());
}
+TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
+ std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
+ " <attr name=\"*android:bar\" />\n"
+ " <attr name=\"privAndroid:bat\" />\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(testParse(input));
+ Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(2u, styleable->entries.size());
+
+ EXPECT_TRUE(styleable->entries[0].privateReference);
+ AAPT_ASSERT_TRUE(styleable->entries[0].name);
+ EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package);
+
+ EXPECT_TRUE(styleable->entries[1].privateReference);
+ AAPT_ASSERT_TRUE(styleable->entries[1].name);
+ EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package);
+}
+
TEST_F(ResourceParserTest, ParseArray) {
std::string input = "<array name=\"foo\">\n"
" <item>@string/ref</item>\n"
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index deafe20..73d8585 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -190,22 +190,32 @@
bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
std::unique_ptr<Value> value, IDiagnostics* diag) {
- return addResourceImpl(name, {}, config, std::move(value), kValidNameChars, diag);
+ return addResourceImpl(name, {}, config, std::move(value), kValidNameChars,
+ resolveValueCollision, diag);
}
bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
const ConfigDescription& config, std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, resId, config, std::move(value), kValidNameChars, diag);
+ return addResourceImpl(name, resId, config, std::move(value), kValidNameChars,
+ resolveValueCollision, diag);
}
bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const StringPiece16& path,
IDiagnostics* diag) {
+ return addFileReference(name, config, source, path, resolveValueCollision, diag);
+}
+
+bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece16& path,
+ std::function<int(Value*,Value*)> conflictResolver,
+ IDiagnostics* diag) {
std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
stringPool.makeRef(path));
fileRef->setSource(source);
- return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars, diag);
+ return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars,
+ conflictResolver, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
@@ -213,7 +223,7 @@
std::unique_ptr<Value> value,
IDiagnostics* diag) {
return addResourceImpl(name, ResourceId{}, config, std::move(value), kValidNameMangledChars,
- diag);
+ resolveValueCollision, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
@@ -221,12 +231,17 @@
const ConfigDescription& config,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars, diag);
+ return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars,
+ resolveValueCollision, diag);
}
-bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
- const char16_t* validChars, IDiagnostics* diag) {
+bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
+ const ResourceId resId,
+ const ConfigDescription& config,
+ std::unique_ptr<Value> value,
+ const char16_t* validChars,
+ std::function<int(Value*,Value*)> conflictResolver,
+ IDiagnostics* diag) {
assert(value && "value can't be nullptr");
assert(diag && "diagnostics can't be nullptr");
@@ -289,7 +304,7 @@
// This resource did not exist before, add it.
entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
} else {
- int collisionResult = resolveValueCollision(iter->value.get(), value.get());
+ int collisionResult = conflictResolver(iter->value.get(), value.get());
if (collisionResult > 0) {
// Take the incoming value.
iter->value = std::move(value);
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 980504b..6b7b07e 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -163,7 +163,12 @@
IDiagnostics* diag);
bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece16& path, IDiagnostics* diag);
+ const Source& source, const StringPiece16& path,
+ IDiagnostics* diag);
+
+ bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece16& path,
+ std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag);
/**
* Same as addResource, but doesn't verify the validity of the name. This is used
@@ -221,9 +226,14 @@
private:
ResourceTablePackage* findOrCreatePackage(const StringPiece16& name);
- bool addResourceImpl(const ResourceNameRef& name, ResourceId resId,
- const ConfigDescription& config, std::unique_ptr<Value> value,
- const char16_t* validChars, IDiagnostics* diag);
+ bool addResourceImpl(const ResourceNameRef& name,
+ ResourceId resId,
+ const ConfigDescription& config,
+ std::unique_ptr<Value> value,
+ const char16_t* validChars,
+ std::function<int(Value*,Value*)> conflictResolver,
+ IDiagnostics* diag);
+
bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId,
const Symbol& symbol, const char16_t* validChars, IDiagnostics* diag);
};
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index d3c3c10..ffe6595 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -15,6 +15,7 @@
*/
#include "ResourceUtils.h"
+#include "flatten/ResourceTypeExtensions.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
@@ -23,22 +24,64 @@
namespace aapt {
namespace ResourceUtils {
-void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
StringPiece16* outType, StringPiece16* outEntry) {
+ bool hasPackageSeparator = false;
+ bool hasTypeSeparator = false;
const char16_t* start = str.data();
const char16_t* end = start + str.size();
const char16_t* current = start;
while (current != end) {
if (outType->size() == 0 && *current == u'/') {
+ hasTypeSeparator = true;
outType->assign(start, current - start);
start = current + 1;
} else if (outPackage->size() == 0 && *current == u':') {
+ hasPackageSeparator = true;
outPackage->assign(start, current - start);
start = current + 1;
}
current++;
}
outEntry->assign(start, end - start);
+
+ return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
+}
+
+bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) {
+ size_t offset = 0;
+ bool priv = false;
+ if (str.data()[0] == u'*') {
+ priv = true;
+ offset = 1;
+ }
+
+ StringPiece16 package;
+ StringPiece16 type;
+ StringPiece16 entry;
+ if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
+ return false;
+ }
+
+ const ResourceType* parsedType = parseResourceType(type);
+ if (!parsedType) {
+ return false;
+ }
+
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (outRef) {
+ outRef->package = package;
+ outRef->type = *parsedType;
+ outRef->entry = entry;
+ }
+
+ if (outPrivate) {
+ *outPrivate = priv;
+ }
+ return true;
}
bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate,
@@ -55,33 +98,30 @@
if (trimmedStr.data()[1] == u'+') {
create = true;
offset += 1;
- } else if (trimmedStr.data()[1] == u'*') {
- priv = true;
- offset += 1;
}
- StringPiece16 package;
- StringPiece16 type;
- StringPiece16 entry;
- extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), &package, &type,
- &entry);
- const ResourceType* parsedType = parseResourceType(type);
- if (!parsedType) {
+ ResourceNameRef name;
+ if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
+ &name, &priv)) {
return false;
}
- if (create && *parsedType != ResourceType::kId) {
+ if (create && priv) {
return false;
}
- if (outRef != nullptr) {
- outRef->package = package;
- outRef->type = *parsedType;
- outRef->entry = entry;
+ if (create && name.type != ResourceType::kId) {
+ return false;
}
+
+ if (outRef) {
+ *outRef = name;
+ }
+
if (outCreate) {
*outCreate = create;
}
+
if (outPrivate) {
*outPrivate = priv;
}
@@ -104,20 +144,33 @@
StringPiece16 package;
StringPiece16 type;
StringPiece16 entry;
- extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry);
+ if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1),
+ &package, &type, &entry)) {
+ return false;
+ }
if (!type.empty() && type != u"attr") {
return false;
}
- outRef->package = package;
- outRef->type = ResourceType::kAttr;
- outRef->entry = entry;
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (outRef) {
+ outRef->package = package;
+ outRef->type = ResourceType::kAttr;
+ outRef->entry = entry;
+ }
return true;
}
return false;
}
+bool isAttributeReference(const StringPiece16& str) {
+ return tryParseAttributeReference(str, nullptr);
+}
+
/*
* Style parent's are a bit different. We accept the following formats:
*
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 851edc8..f93a4c7 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -34,10 +34,18 @@
*
* where the package can be empty. Validation must be performed on each
* individual extracted piece to verify that the pieces are valid.
+ * Returns false if there was no package but a ':' was present.
*/
-void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
StringPiece16* outType, StringPiece16* outEntry);
+/**
+ * Returns true if the string was parsed as a resource name ([*][package:]type/name), with
+ * `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix
+ * was present.
+ */
+bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, bool* outPrivate);
+
/*
* Returns true if the string was parsed as a reference (@[+][package:]type/name), with
* `outReference` set to the parsed reference.
@@ -54,12 +62,17 @@
bool isReference(const StringPiece16& str);
/*
- * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
+ * Returns true if the string was parsed as an attribute reference (?[package:][type/]name),
* with `outReference` set to the parsed reference.
*/
bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outReference);
/**
+ * Returns true if the string is in the form of an attribute reference(?[package:][type/]name).
+ */
+bool isAttributeReference(const StringPiece16& str);
+
+/**
* Returns true if the value is a boolean, putting the result in `outValue`.
*/
bool tryParseBool(const StringPiece16& str, bool* outValue);
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 7de8f41..4bbfc32 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,15 +16,30 @@
#include "Resource.h"
#include "ResourceUtils.h"
-
#include "test/Common.h"
#include <gtest/gtest.h>
namespace aapt {
+TEST(ResourceUtilsTest, ParseResourceName) {
+ ResourceNameRef actual;
+ bool actualPriv = false;
+ EXPECT_TRUE(ResourceUtils::parseResourceName(u"android:color/foo", &actual, &actualPriv));
+ EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
+ EXPECT_FALSE(actualPriv);
+
+ EXPECT_TRUE(ResourceUtils::parseResourceName(u"color/foo", &actual, &actualPriv));
+ EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, u"foo"), actual);
+ EXPECT_FALSE(actualPriv);
+
+ EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv));
+ EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
+ EXPECT_TRUE(actualPriv);
+}
+
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
- ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
+ ResourceNameRef expected({}, ResourceType::kColor, u"foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
@@ -35,7 +50,7 @@
}
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
- ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+ ResourceNameRef expected(u"android", ResourceType::kColor, u"foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
@@ -47,7 +62,7 @@
}
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
- ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+ ResourceNameRef expected(u"android", ResourceType::kColor, u"foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
@@ -59,7 +74,7 @@
}
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
- ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+ ResourceNameRef expected(u"android", ResourceType::kId, u"foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
@@ -71,7 +86,7 @@
}
TEST(ResourceUtilsTest, ParsePrivateReference) {
- ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+ ResourceNameRef expected(u"android", ResourceType::kId, u"foo");
ResourceNameRef actual;
bool create = false;
bool privateRef = false;
@@ -90,9 +105,29 @@
&privateRef));
}
+TEST(ResourceUtilsTest, ParseAttributeReferences) {
+ EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android"));
+ EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:foo"));
+ EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?attr/foo"));
+ EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:attr/foo"));
+}
+
+TEST(ResourceUtilsTest, FailParseIncompleteReference) {
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?style/foo"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:style/foo"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:attr/"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/foo"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/foo"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?attr/"));
+ EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?/foo"));
+}
+
TEST(ResourceUtilsTest, ParseStyleParentReference) {
- const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" };
- const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" };
+ const ResourceName kAndroidStyleFooName(u"android", ResourceType::kStyle, u"foo");
+ const ResourceName kStyleFooName({}, ResourceType::kStyle, u"foo");
std::string errStr;
Maybe<Reference> ref = ResourceUtils::parseStyleParentReference(u"@android:style/foo", &errStr);
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 8acff0d..04c375f 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -15,9 +15,9 @@
*/
#include "Resource.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-
#include "util/Util.h"
#include "flatten/ResourceTypeExtensions.h"
@@ -71,27 +71,23 @@
}
bool Reference::flatten(android::Res_value* outValue) const {
- outValue->dataType = (referenceType == Reference::Type::kResource)
- ? android::Res_value::TYPE_REFERENCE
- : android::Res_value::TYPE_ATTRIBUTE;
+ outValue->dataType = (referenceType == Reference::Type::kResource) ?
+ android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
outValue->data = util::hostToDevice32(id ? id.value().id : 0);
return true;
}
Reference* Reference::clone(StringPool* /*newPool*/) const {
- Reference* ref = new Reference();
- ref->mComment = mComment;
- ref->mSource = mSource;
- ref->referenceType = referenceType;
- ref->name = name;
- ref->id = id;
- return ref;
+ return new Reference(*this);
}
void Reference::print(std::ostream* out) const {
*out << "(reference) ";
if (referenceType == Reference::Type::kResource) {
*out << "@";
+ if (privateReference) {
+ *out << "*";
+ }
} else {
*out << "?";
}
@@ -116,10 +112,7 @@
}
Id* Id::clone(StringPool* /*newPool*/) const {
- Id* id = new Id();
- id->mComment = mComment;
- id->mSource = mSource;
- return id;
+ return new Id(*this);
}
void Id::print(std::ostream* out) const {
@@ -214,10 +207,7 @@
}
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
- BinaryPrimitive* bp = new BinaryPrimitive(value);
- bp->mComment = mComment;
- bp->mSource = mSource;
- return bp;
+ return new BinaryPrimitive(*this);
}
void BinaryPrimitive::print(std::ostream* out) const {
@@ -226,7 +216,7 @@
*out << "(null)";
break;
case android::Res_value::TYPE_INT_DEC:
- *out << "(integer) " << value.data;
+ *out << "(integer) " << static_cast<int32_t>(value.data);
break;
case android::Res_value::TYPE_INT_HEX:
*out << "(integer) " << std::hex << value.data << std::dec;
@@ -247,7 +237,10 @@
}
}
-Attribute::Attribute(bool w, uint32_t t) : weak(w), typeMask(t) {
+Attribute::Attribute(bool w, uint32_t t) :
+ weak(w), typeMask(t),
+ minInt(std::numeric_limits<int32_t>::min()),
+ maxInt(std::numeric_limits<int32_t>::max()) {
}
bool Attribute::isWeak() const {
@@ -255,12 +248,7 @@
}
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
- Attribute* attr = new Attribute(weak);
- attr->mComment = mComment;
- attr->mSource = mSource;
- attr->typeMask = typeMask;
- std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
- return attr;
+ return new Attribute(*this);
}
void Attribute::printMask(std::ostream* out) const {
@@ -376,6 +364,81 @@
}
}
+static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
+ const Item* value) {
+ *msg << "expected";
+ if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ *msg << " boolean";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
+ *msg << " color";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
+ *msg << " dimension";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
+ *msg << " enum";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
+ *msg << " flags";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
+ *msg << " float";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
+ *msg << " fraction";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
+ *msg << " integer";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ *msg << " reference";
+ }
+
+ if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
+ *msg << " string";
+ }
+
+ *msg << " but got " << *value;
+}
+
+bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
+ android::Res_value val = {};
+ item->flatten(&val);
+
+ // Always allow references.
+ const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
+ if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
+ if (outMsg) {
+ buildAttributeMismatchMessage(outMsg, this, item);
+ }
+ return false;
+
+ } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
+ android::ResTable_map::TYPE_INTEGER) {
+ if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
+ if (outMsg) {
+ *outMsg << *item << " is less than minimum integer " << minInt;
+ }
+ return false;
+ } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
+ if (outMsg) {
+ *outMsg << *item << " is greater than maximum integer " << maxInt;
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
Style* Style::clone(StringPool* newPool) const {
Style* style = new Style();
style->parent = parent;
@@ -450,11 +513,7 @@
}
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
- Styleable* styleable = new Styleable();
- styleable->mComment = mComment;
- styleable->mSource = mSource;
- std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
- return styleable;
+ return new Styleable(*this);
}
void Styleable::print(std::ostream* out) const {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 7ae346a..a038282 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -17,9 +17,10 @@
#ifndef AAPT_RESOURCE_VALUES_H
#define AAPT_RESOURCE_VALUES_H
-#include "util/Maybe.h"
+#include "Diagnostics.h"
#include "Resource.h"
#include "StringPool.h"
+#include "util/Maybe.h"
#include <array>
#include <androidfw/ResourceTypes.h>
@@ -233,8 +234,8 @@
bool weak;
uint32_t typeMask;
- uint32_t minInt;
- uint32_t maxInt;
+ int32_t minInt;
+ int32_t maxInt;
std::vector<Symbol> symbols;
Attribute(bool w, uint32_t t = 0u);
@@ -243,6 +244,7 @@
Attribute* clone(StringPool* newPool) const override;
void printMask(std::ostream* out) const;
void print(std::ostream* out) const override;
+ bool matches(const Item* item, DiagMessage* outMsg) const;
};
struct Style : public BaseValue<Style> {
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 8af203c..319528e 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -58,6 +58,10 @@
return out;
}
+inline bool operator==(const Source& lhs, const Source& rhs) {
+ return lhs.path == rhs.path && lhs.line == rhs.line;
+}
+
inline bool operator<(const Source& lhs, const Source& rhs) {
int cmp = lhs.path.compare(rhs.path);
if (cmp < 0) return true;
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 39088bc..17a658e 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -19,9 +19,6 @@
#include "Flags.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
-#include "XmlDom.h"
-#include "XmlPullParser.h"
-
#include "compile/IdAssigner.h"
#include "compile/Png.h"
#include "compile/XmlIdCollector.h"
@@ -31,6 +28,8 @@
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlPullParser.h"
#include <fstream>
#include <string>
@@ -131,7 +130,7 @@
// Parse the values file from XML.
- XmlPullParser xmlParser(fin);
+ xml::XmlPullParser xmlParser(fin);
ResourceParserOptions parserOptions;
parserOptions.product = options.product;
@@ -191,7 +190,7 @@
static bool compileXml(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, const std::string& outputPath) {
- std::unique_ptr<XmlResource> xmlRes;
+ std::unique_ptr<xml::XmlResource> xmlRes;
{
std::ifstream fin(pathData.source.path, std::ifstream::binary);
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index dfdf710..f40689e 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -16,9 +16,8 @@
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "XmlDom.h"
-
#include "compile/XmlIdCollector.h"
+#include "xml/XmlDom.h"
#include <algorithm>
#include <vector>
@@ -61,7 +60,7 @@
} // namespace
-bool XmlIdCollector::consume(IAaptContext* context, XmlResource* xmlRes) {
+bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) {
xmlRes->file.exportedSymbols.clear();
IdCollector collector(&xmlRes->file.exportedSymbols);
xmlRes->root->accept(&collector);
diff --git a/tools/aapt2/compile/XmlIdCollector.h b/tools/aapt2/compile/XmlIdCollector.h
index 96a58f2..1b14944 100644
--- a/tools/aapt2/compile/XmlIdCollector.h
+++ b/tools/aapt2/compile/XmlIdCollector.h
@@ -18,11 +18,12 @@
#define AAPT_XMLIDCOLLECTOR_H
#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
namespace aapt {
struct XmlIdCollector : public IXmlResourceConsumer {
- bool consume(IAaptContext* context, XmlResource* xmlRes) override;
+ bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
};
} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp
index c703f451..45b7af2 100644
--- a/tools/aapt2/compile/XmlIdCollector_test.cpp
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -26,7 +26,7 @@
TEST(XmlIdCollectorTest, CollectsIds) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/foo"
text="@+id/bar">
@@ -50,7 +50,7 @@
TEST(XmlIdCollectorTest, DontCollectNonIds) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
XmlIdCollector collector;
ASSERT_TRUE(collector.consume(context.get(), doc.get()));
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
index c1ff556..02bff2c 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -62,7 +62,29 @@
* A raw string value that hasn't had its escape sequences
* processed nor whitespace removed.
*/
- TYPE_RAW_STRING = 0xfe
+ TYPE_RAW_STRING = 0xfe,
+ };
+};
+
+/**
+ * New types for a ResTable_map.
+ */
+struct ExtendedResTableMapTypes {
+ enum {
+ /**
+ * Type that contains the source path of the next item in the map.
+ */
+ ATTR_SOURCE_PATH = Res_MAKEINTERNAL(0xffff),
+
+ /**
+ * Type that contains the source line of the next item in the map.
+ */
+ ATTR_SOURCE_LINE = Res_MAKEINTERNAL(0xfffe),
+
+ /**
+ * Type that contains the comment of the next item in the map.
+ */
+ ATTR_COMMENT = Res_MAKEINTERNAL(0xfffd)
};
};
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 6b90fb2..f42e6b7 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -78,10 +78,18 @@
explicit SymbolWriter(StringPool* pool) : mPool(pool) {
}
- void addSymbol(const ResourceNameRef& name, size_t offset) {
- symbols.push_back(Entry{ mPool->makeRef(name.package.toString() + u":" +
- toString(name.type).toString() + u"/" +
- name.entry.toString()), offset });
+ void addSymbol(const Reference& ref, size_t offset) {
+ const ResourceName& name = ref.name.value();
+ std::u16string fullName;
+ if (ref.privateReference) {
+ fullName += u"*";
+ }
+
+ if (!name.package.empty()) {
+ fullName += name.package + u":";
+ }
+ fullName += toString(name.type).toString() + u"/" + name.entry;
+ symbols.push_back(Entry{ mPool->makeRef(fullName), offset });
}
void shiftAllOffsets(size_t offset) {
@@ -100,20 +108,27 @@
SymbolWriter* mSymbols;
FlatEntry* mEntry;
BigBuffer* mBuffer;
+ StringPool* mSourcePool;
+ StringPool* mCommentPool;
+ bool mUseExtendedChunks;
+
size_t mEntryCount = 0;
Maybe<uint32_t> mParentIdent;
Maybe<ResourceNameRef> mParentName;
- MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer) :
- mSymbols(symbols), mEntry(entry), mBuffer(buffer) {
+ MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer,
+ StringPool* sourcePool, StringPool* commentPool,
+ bool useExtendedChunks) :
+ mSymbols(symbols), mEntry(entry), mBuffer(buffer), mSourcePool(sourcePool),
+ mCommentPool(commentPool), mUseExtendedChunks(useExtendedChunks) {
}
void flattenKey(Reference* key, ResTable_map* outEntry) {
- if (!key->id) {
+ if (!key->id || (key->privateReference && mUseExtendedChunks)) {
assert(key->name && "reference must have a name");
outEntry->name.ident = util::hostToDevice32(0);
- mSymbols->addSymbol(key->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
+ mSymbols->addSymbol(*key, (mBuffer->size() - sizeof(ResTable_map)) +
offsetof(ResTable_map, name));
} else {
outEntry->name.ident = util::hostToDevice32(key->id.value().id);
@@ -121,16 +136,21 @@
}
void flattenValue(Item* value, ResTable_map* outEntry) {
+ bool privateRef = false;
if (Reference* ref = valueCast<Reference>(value)) {
- if (!ref->id) {
+ privateRef = ref->privateReference && mUseExtendedChunks;
+ if (!ref->id || privateRef) {
assert(ref->name && "reference must have a name");
- mSymbols->addSymbol(ref->name.value(), (mBuffer->size() - sizeof(ResTable_map)) +
+ mSymbols->addSymbol(*ref, (mBuffer->size() - sizeof(ResTable_map)) +
offsetof(ResTable_map, value) + offsetof(Res_value, data));
}
}
bool result = value->flatten(&outEntry->value);
+ if (privateRef) {
+ outEntry->value.data = 0;
+ }
assert(result && "flatten failed");
}
@@ -142,6 +162,32 @@
mEntryCount++;
}
+ void flattenMetaData(Value* value) {
+ if (!mUseExtendedChunks) {
+ return;
+ }
+
+ Reference key(ResourceId{ ExtendedResTableMapTypes::ATTR_SOURCE_PATH });
+ StringPool::Ref sourcePathRef = mSourcePool->makeRef(
+ util::utf8ToUtf16(value->getSource().path));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC,
+ static_cast<uint32_t>(sourcePathRef.getIndex()));
+ flattenEntry(&key, &val);
+
+ if (value->getSource().line) {
+ key.id = ResourceId(ExtendedResTableMapTypes::ATTR_SOURCE_LINE);
+ val.value.data = static_cast<uint32_t>(value->getSource().line.value());
+ flattenEntry(&key, &val);
+ }
+
+ if (!value->getComment().empty()) {
+ key.id = ResourceId(ExtendedResTableMapTypes::ATTR_COMMENT);
+ StringPool::Ref commentRef = mCommentPool->makeRef(value->getComment());
+ val.value.data = static_cast<uint32_t>(commentRef.getIndex());
+ flattenEntry(&key, &val);
+ }
+ }
+
void visit(Attribute* attr) override {
{
Reference key(ResourceId{ ResTable_map::ATTR_TYPE });
@@ -149,6 +195,18 @@
flattenEntry(&key, &val);
}
+ if (attr->minInt != std::numeric_limits<int32_t>::min()) {
+ Reference key(ResourceId{ ResTable_map::ATTR_MIN });
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->minInt));
+ flattenEntry(&key, &val);
+ }
+
+ if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
+ Reference key(ResourceId{ ResTable_map::ATTR_MAX });
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->maxInt));
+ flattenEntry(&key, &val);
+ }
+
for (Attribute::Symbol& s : attr->symbols) {
BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
flattenEntry(&s.symbol, &val);
@@ -169,7 +227,8 @@
void visit(Style* style) override {
if (style->parent) {
- if (!style->parent.value().id) {
+ bool privateRef = style->parent.value().privateReference && mUseExtendedChunks;
+ if (!style->parent.value().id || privateRef) {
assert(style->parent.value().name && "reference must have a name");
mParentName = style->parent.value().name;
} else {
@@ -182,6 +241,7 @@
for (Style::Entry& entry : style->entries) {
flattenEntry(&entry.key, entry.value.get());
+ flattenMetaData(&entry.key);
}
}
@@ -189,6 +249,7 @@
for (auto& attrRef : styleable->entries) {
BinaryPrimitive val(Res_value{});
flattenEntry(&attrRef, &val);
+ flattenMetaData(&attrRef);
}
}
@@ -198,6 +259,7 @@
flattenValue(item.get(), outEntry);
outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
mEntryCount++;
+ flattenMetaData(item.get());
}
}
@@ -241,6 +303,7 @@
Reference key(q);
flattenEntry(&key, plural->values[i].get());
+ flattenMetaData(plural->values[i].get());
}
}
};
@@ -339,21 +402,29 @@
bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
if (Item* item = valueCast<Item>(entry->value)) {
writeEntry<ResTable_entry, true>(entry, buffer);
+ bool privateRef = false;
if (Reference* ref = valueCast<Reference>(entry->value)) {
- if (!ref->id) {
+ // If there is no ID or the reference is private and we allow extended chunks,
+ // write out a 0 and mark the symbol table with the name of the reference.
+ privateRef = (ref->privateReference && mOptions.useExtendedChunks);
+ if (!ref->id || privateRef) {
assert(ref->name && "reference must have at least a name");
- mSymbols->addSymbol(ref->name.value(),
- buffer->size() + offsetof(Res_value, data));
+ mSymbols->addSymbol(*ref, buffer->size() + offsetof(Res_value, data));
}
}
Res_value* outValue = buffer->nextBlock<Res_value>();
bool result = item->flatten(outValue);
assert(result && "flatten failed");
+ if (privateRef) {
+ // Force the value of 0 so we look up the symbol at unflatten time.
+ outValue->data = 0;
+ }
outValue->size = util::hostToDevice16(sizeof(*outValue));
} else {
const size_t beforeEntry = buffer->size();
ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
- MapFlattenVisitor visitor(mSymbols, entry, buffer);
+ MapFlattenVisitor visitor(mSymbols, entry, buffer, mSourcePool, mSourcePool,
+ mOptions.useExtendedChunks);
entry->value->accept(&visitor);
outEntry->count = util::hostToDevice32(visitor.mEntryCount);
if (visitor.mParentName) {
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 68a1f47..7030603 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -15,11 +15,11 @@
*/
#include "flatten/TableFlattener.h"
+#include "test/Builders.h"
+#include "test/Context.h"
#include "unflatten/BinaryResourceParser.h"
#include "util/Util.h"
-#include "test/Builders.h"
-#include "test/Context.h"
#include <gtest/gtest.h>
@@ -262,4 +262,55 @@
EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@com.app.test:color/green"));
}
+TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
+ Attribute attr(false);
+ attr.typeMask = android::ResTable_map::TYPE_INTEGER;
+ attr.minInt = 10;
+ attr.maxInt = 23;
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addValue(u"@android:attr/foo", ResourceId(0x01010000),
+ util::make_unique<Attribute>(attr))
+ .build();
+
+ ResourceTable result;
+ ASSERT_TRUE(flatten(table.get(), &result));
+
+ Attribute* actualAttr = test::getValue<Attribute>(&result, u"@android:attr/foo");
+ ASSERT_NE(nullptr, actualAttr);
+ EXPECT_EQ(attr.isWeak(), actualAttr->isWeak());
+ EXPECT_EQ(attr.typeMask, actualAttr->typeMask);
+ EXPECT_EQ(attr.minInt, actualAttr->minInt);
+ EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
+}
+
+TEST_F(TableFlattenerTest, FlattenSourceAndCommentsForChildrenOfCompoundValues) {
+ Style style;
+ Reference key(test::parseNameOrDie(u"@android:attr/foo"));
+ key.id = ResourceId(0x01010000);
+ key.setSource(Source("test").withLine(2));
+ key.setComment(StringPiece16(u"comment"));
+ style.entries.push_back(Style::Entry{ key, util::make_unique<Id>() });
+
+ test::ResourceTableBuilder builder = test::ResourceTableBuilder();
+ std::unique_ptr<ResourceTable> table = builder
+ .setPackageId(u"android", 0x01)
+ .addValue(u"@android:attr/foo", ResourceId(0x01010000),
+ test::AttributeBuilder().build())
+ .addValue(u"@android:style/foo", ResourceId(0x01020000),
+ std::unique_ptr<Style>(style.clone(builder.getStringPool())))
+ .build();
+
+ ResourceTable result;
+ ASSERT_TRUE(flatten(table.get(), &result));
+
+ Style* actualStyle = test::getValue<Style>(&result, u"@android:style/foo");
+ ASSERT_NE(nullptr, actualStyle);
+ ASSERT_EQ(1u, actualStyle->entries.size());
+
+ Reference* actualKey = &actualStyle->entries[0].key;
+ EXPECT_EQ(key.getSource(), actualKey->getSource());
+ EXPECT_EQ(key.getComment(), actualKey->getComment());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 4efb08b..8219462 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -15,15 +15,14 @@
*/
#include "SdkConstants.h"
-#include "XmlDom.h"
-
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
#include "flatten/XmlFlattener.h"
+#include "xml/XmlDom.h"
#include <androidfw/ResourceTypes.h>
-#include <vector>
#include <utils/misc.h>
+#include <vector>
using namespace android;
@@ -306,7 +305,7 @@
return true;
}
-bool XmlFlattener::consume(IAaptContext* context, XmlResource* resource) {
+bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) {
if (!resource->root) {
return false;
}
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
index b1fb3a7..a688ac9 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -17,16 +17,12 @@
#ifndef AAPT_FLATTEN_XMLFLATTENER_H
#define AAPT_FLATTEN_XMLFLATTENER_H
-#include "util/BigBuffer.h"
-
#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
+#include "xml/XmlDom.h"
namespace aapt {
-namespace xml {
-struct Node;
-}
-
struct XmlFlattenerOptions {
/**
* Keep attribute raw string values along with typed values.
@@ -45,7 +41,7 @@
mBuffer(buffer), mOptions(options) {
}
- bool consume(IAaptContext* context, XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
private:
BigBuffer* mBuffer;
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 318bcdd..8648879 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -16,11 +16,10 @@
#include "flatten/XmlFlattener.h"
#include "link/Linkers.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
#include "test/Builders.h"
#include "test/Context.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
#include <gtest/gtest.h>
@@ -45,7 +44,7 @@
.build();
}
- ::testing::AssertionResult flatten(XmlResource* doc, android::ResXMLTree* outTree,
+ ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
XmlFlattenerOptions options = {}) {
BigBuffer buffer(1024);
XmlFlattener flattener(&buffer, options);
@@ -65,7 +64,7 @@
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:test="http://com.test"
attr="hey">
<Layout test:hello="hi" />
@@ -144,7 +143,7 @@
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="1dp"
android:colorAccent="#ffffff"/>)EOF");
@@ -169,7 +168,7 @@
}
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/id"
class="str"
@@ -192,7 +191,7 @@
* namespace.
*/
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
android::ResXMLTree tree;
ASSERT_TRUE(flatten(doc.get(), &tree));
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index d5a2b38..da96b84 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -17,12 +17,10 @@
#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "XmlPullParser.h"
-
#include "java/AnnotationProcessor.h"
-
#include "test/Builders.h"
#include "test/Context.h"
+#include "xml/XmlPullParser.h"
#include <gtest/gtest.h>
@@ -42,7 +40,7 @@
options);
std::stringstream in;
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
- XmlPullParser xmlParser(in);
+ xml::XmlPullParser xmlParser(in);
if (parser.parse(&xmlParser)) {
return ::testing::AssertionSuccess();
}
diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h
index b8886f9..04e1274 100644
--- a/tools/aapt2/java/ClassDefinitionWriter.h
+++ b/tools/aapt2/java/ClassDefinitionWriter.h
@@ -17,6 +17,7 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
+#include "Resource.h"
#include "java/AnnotationProcessor.h"
#include "util/StringPiece.h"
#include "util/Util.h"
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index d963d89..a9b4c14 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -15,12 +15,11 @@
*/
#include "Source.h"
-#include "XmlDom.h"
-
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinitionWriter.h"
#include "java/ManifestClassGenerator.h"
#include "util/Maybe.h"
+#include "xml/XmlDom.h"
#include <algorithm>
@@ -80,7 +79,7 @@
}
bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
- XmlResource* res, std::ostream* out) {
+ xml::XmlResource* res, std::ostream* out) {
xml::Element* el = xml::findRootElement(res->root.get());
if (!el) {
return false;
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index 0f0998f..226ed23 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -18,15 +18,15 @@
#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
#include "Diagnostics.h"
-#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
+#include "xml/XmlDom.h"
#include <iostream>
namespace aapt {
struct ManifestClassGenerator {
- bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
+ bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res,
std::ostream* out);
};
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index 4081287..fc57ae6f 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -15,7 +15,6 @@
*/
#include "java/ManifestClassGenerator.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -25,7 +24,7 @@
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<permission android:name="android.permission.ACCESS_INTERNET" />
<permission android:name="android.DO_DANGEROUS_THINGS" />
@@ -75,7 +74,7 @@
TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required to access the internet.
Added in API 1. -->
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 4431477..c096854 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-#include "XmlDom.h"
-
#include "java/ProguardRules.h"
#include "util/Util.h"
+#include "xml/XmlDom.h"
#include <memory>
#include <string>
@@ -40,11 +39,11 @@
virtual void visit(xml::Element* node) override {
if (!node->namespaceUri.empty()) {
- Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
+ Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace(
node->namespaceUri);
if (maybePackage) {
// This is a custom view, let's figure out the class name from this.
- std::u16string package = maybePackage.value() + u"." + node->name;
+ std::u16string package = maybePackage.value().package + u"." + node->name;
if (util::isJavaClassName(package)) {
addClass(node->lineNumber, package);
}
@@ -185,7 +184,8 @@
std::u16string mPackage;
};
-bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
+bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
+ KeepSet* keepSet) {
ManifestVisitor visitor(source, keepSet);
if (res->root) {
res->root->accept(&visitor);
@@ -194,7 +194,7 @@
return false;
}
-bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
+bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
if (!res->root) {
return false;
}
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index be61eb9..aafffd3 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -19,8 +19,7 @@
#include "Resource.h"
#include "Source.h"
-
-#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
#include <map>
#include <ostream>
@@ -47,8 +46,8 @@
std::map<std::u16string, std::set<Source>> mKeepMethodSet;
};
-bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet);
-bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet);
+bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
+bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet);
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 9ce3734..9850ae5 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -18,8 +18,6 @@
#include "Debug.h"
#include "Flags.h"
#include "NameMangler.h"
-#include "XmlDom.h"
-
#include "compile/IdAssigner.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
@@ -28,6 +26,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ReferenceLinker.h"
#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
@@ -36,6 +35,7 @@
#include "unflatten/FileExportHeaderReader.h"
#include "util/Files.h"
#include "util/StringPiece.h"
+#include "xml/XmlDom.h"
#include <fstream>
#include <sys/stat.h>
@@ -48,8 +48,9 @@
std::string outputPath;
std::string manifestPath;
std::vector<std::string> includePaths;
+ std::vector<std::string> overlayFiles;
Maybe<std::string> generateJavaClassPath;
- std::vector<std::string> extraJavaPackages;
+ std::set<std::string> extraJavaPackages;
Maybe<std::string> generateProguardRulesPath;
bool noAutoVersion = false;
bool staticLib = false;
@@ -88,9 +89,11 @@
}
};
-struct LinkCommand {
- LinkOptions mOptions;
- LinkContext mContext;
+class LinkCommand {
+public:
+ LinkCommand(const LinkOptions& options) :
+ mOptions(options), mContext(), mFinalTable() {
+ }
std::string buildResourceFileName(const ResourceFile& resFile) {
std::stringstream out;
@@ -117,8 +120,7 @@
AssetManagerSymbolTableBuilder builder;
for (const std::string& path : mOptions.includePaths) {
if (mOptions.verbose) {
- mContext.getDiagnostics()->note(
- DiagMessage(Source{ path }) << "loading include path");
+ mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path");
}
std::unique_ptr<android::AssetManager> assetManager =
@@ -126,7 +128,7 @@
int32_t cookie = 0;
if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
mContext.getDiagnostics()->error(
- DiagMessage(Source{ path }) << "failed to load include path");
+ DiagMessage(path) << "failed to load include path");
return {};
}
builder.add(std::move(assetManager));
@@ -141,12 +143,12 @@
std::string errorStr;
Maybe<android::FileMap> map = file::mmapPath(input, &errorStr);
if (!map) {
- mContext.getDiagnostics()->error(DiagMessage(Source{ input }) << errorStr);
+ mContext.getDiagnostics()->error(DiagMessage(input) << errorStr);
return {};
}
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(&mContext, table.get(), Source{ input },
+ BinaryResourceParser parser(&mContext, table.get(), Source(input),
map.value().getDataPtr(), map.value().getDataLength());
if (!parser.parse()) {
return {};
@@ -157,20 +159,20 @@
/**
* Inflates an XML file from the source path.
*/
- std::unique_ptr<XmlResource> loadXml(const std::string& path) {
+ std::unique_ptr<xml::XmlResource> loadXml(const std::string& path) {
std::ifstream fin(path, std::ifstream::binary);
if (!fin) {
- mContext.getDiagnostics()->error(DiagMessage(Source{ path }) << strerror(errno));
+ mContext.getDiagnostics()->error(DiagMessage(path) << strerror(errno));
return {};
}
- return xml::inflate(&fin, mContext.getDiagnostics(), Source{ path });
+ return xml::inflate(&fin, mContext.getDiagnostics(), Source(path));
}
/**
* Inflates a binary XML file from the source path.
*/
- std::unique_ptr<XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
+ std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
// Read header for symbol info and export info.
std::string errorStr;
Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
@@ -186,7 +188,7 @@
return {};
}
- std::unique_ptr<XmlResource> xmlRes = xml::inflate(
+ std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
(const uint8_t*) maybeF.value().getDataPtr() + (size_t) offset,
maybeF.value().getDataLength() - offset,
mContext.getDiagnostics(), Source(path));
@@ -243,7 +245,7 @@
return true;
}
- Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) {
+ Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
// Make sure the first element is <manifest> with package attribute.
if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
@@ -255,9 +257,9 @@
return {};
}
- bool verifyNoExternalPackages(ResourceTable* table) {
+ bool verifyNoExternalPackages() {
bool error = false;
- for (const auto& package : table->packages) {
+ for (const auto& package : mFinalTable.packages) {
if (mContext.getCompilationPackage() != package->name ||
!package->id || package->id.value() != mContext.getPackageId()) {
// We have a package that is not related to the one we're building!
@@ -307,7 +309,7 @@
return true;
}
- bool flattenXml(XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
+ bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
IArchiveWriter* writer) {
BigBuffer buffer(1024);
XmlFlattenerOptions options = {};
@@ -352,7 +354,7 @@
return true;
}
- bool writeManifestJavaFile(XmlResource* manifestXml) {
+ bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
if (!mOptions.generateJavaClassPath) {
return true;
}
@@ -401,9 +403,106 @@
return true;
}
+ bool mergeStaticLibrary(const std::string& input) {
+ // TODO(adamlesinski): Load resources from a static library APK and merge the table into
+ // TableMerger.
+ mContext.getDiagnostics()->warn(DiagMessage()
+ << "linking static libraries not supported yet: "
+ << input);
+ return true;
+ }
+
+ bool mergeResourceTable(const std::string& input, bool override) {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(DiagMessage() << "linking " << input);
+ }
+
+ std::unique_ptr<ResourceTable> table = loadTable(input);
+ if (!table) {
+ return false;
+ }
+
+ if (!mTableMerger->merge(Source(input), table.get(), override)) {
+ return false;
+ }
+ return true;
+ }
+
+ bool mergeCompiledFile(const std::string& input, ResourceFile&& file, bool override) {
+ if (file.name.package.empty()) {
+ file.name.package = mContext.getCompilationPackage().toString();
+ }
+
+ ResourceNameRef resName = file.name;
+
+ Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(file.name);
+ if (mangledName) {
+ resName = mangledName.value();
+ }
+
+ std::function<int(Value*,Value*)> resolver;
+ if (override) {
+ resolver = [](Value* a, Value* b) -> int {
+ int result = ResourceTable::resolveValueCollision(a, b);
+ if (result == 0) {
+ // Always accept the new value if it would normally conflict (override).
+ result = 1;
+ }
+ return result;
+ };
+ } else {
+ // Otherwise use the default resolution.
+ resolver = ResourceTable::resolveValueCollision;
+ }
+
+ // Add this file to the table.
+ if (!mFinalTable.addFileReference(resName, file.config, file.source,
+ util::utf8ToUtf16(buildResourceFileName(file)),
+ resolver, mContext.getDiagnostics())) {
+ return false;
+ }
+
+ // Add the exports of this file to the table.
+ for (SourcedResourceName& exportedSymbol : file.exportedSymbols) {
+ if (exportedSymbol.name.package.empty()) {
+ exportedSymbol.name.package = mContext.getCompilationPackage().toString();
+ }
+
+ ResourceNameRef resName = exportedSymbol.name;
+
+ Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
+ exportedSymbol.name);
+ if (mangledName) {
+ resName = mangledName.value();
+ }
+
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->setSource(file.source.withLine(exportedSymbol.line));
+ bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
+ mContext.getDiagnostics());
+ if (!result) {
+ return false;
+ }
+ }
+
+ mFilesToProcess.insert(FileToProcess{ std::move(file), Source(input) });
+ return true;
+ }
+
+ bool processFile(const std::string& input, bool override) {
+ if (util::stringEndsWith<char>(input, ".apk")) {
+ return mergeStaticLibrary(input);
+ } else if (util::stringEndsWith<char>(input, ".arsc.flat")) {
+ return mergeResourceTable(input, override);
+ } else if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) {
+ return mergeCompiledFile(input, std::move(maybeF.value()), override);
+ }
+ return false;
+ }
+
int run(const std::vector<std::string>& inputFiles) {
// Load the AndroidManifest.xml
- std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath);
+ std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath);
if (!manifestXml) {
return 1;
}
@@ -438,97 +537,36 @@
return 1;
}
+ mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable);
+
if (mOptions.verbose) {
mContext.getDiagnostics()->note(
DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
<< "with package ID " << std::hex << (int) mContext.mPackageId);
}
- ResourceTable mergedTable;
- TableMerger tableMerger(&mContext, &mergedTable);
- struct FilesToProcess {
- Source source;
- ResourceFile file;
- };
-
- bool error = false;
- std::queue<FilesToProcess> filesToProcess;
for (const std::string& input : inputFiles) {
- if (util::stringEndsWith<char>(input, ".apk")) {
- // TODO(adamlesinski): Load resources from a static library APK
- // Merge the table into TableMerger.
-
- } else if (util::stringEndsWith<char>(input, ".arsc.flat")) {
- if (mOptions.verbose) {
- mContext.getDiagnostics()->note(DiagMessage() << "linking " << input);
- }
-
- std::unique_ptr<ResourceTable> table = loadTable(input);
- if (!table) {
- return 1;
- }
-
- if (!tableMerger.merge(Source(input), table.get())) {
- return 1;
- }
-
- } else {
- // Extract the exported IDs here so we can build the resource table.
- if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) {
- ResourceFile& f = maybeF.value();
-
- if (f.name.package.empty()) {
- f.name.package = mContext.getCompilationPackage().toString();
- }
-
- Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(f.name);
-
- // Add this file to the table.
- if (!mergedTable.addFileReference(mangledName ? mangledName.value() : f.name,
- f.config, f.source,
- util::utf8ToUtf16(buildResourceFileName(f)),
- mContext.getDiagnostics())) {
- error = true;
- }
-
- // Add the exports of this file to the table.
- for (SourcedResourceName& exportedSymbol : f.exportedSymbols) {
- if (exportedSymbol.name.package.empty()) {
- exportedSymbol.name.package = mContext.getCompilationPackage()
- .toString();
- }
-
- Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
- exportedSymbol.name);
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->setSource(f.source.withLine(exportedSymbol.line));
- if (!mergedTable.addResourceAllowMangled(
- mangledName ? mangledName.value() : exportedSymbol.name,
- {}, std::move(id), mContext.getDiagnostics())) {
- error = true;
- }
- }
-
- filesToProcess.push(FilesToProcess{ Source(input), std::move(f) });
- } else {
- return 1;
- }
+ if (!processFile(input, false)) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
+ return 1;
}
}
- if (error) {
- mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
- return 1;
+ for (const std::string& input : mOptions.overlayFiles) {
+ if (!processFile(input, true)) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
+ return 1;
+ }
}
- if (!verifyNoExternalPackages(&mergedTable)) {
+ if (!verifyNoExternalPackages()) {
return 1;
}
if (!mOptions.staticLib) {
PrivateAttributeMover mover;
- if (!mover.consume(&mContext, &mergedTable)) {
+ if (!mover.consume(&mContext, &mFinalTable)) {
mContext.getDiagnostics()->error(
DiagMessage() << "failed moving private attributes");
return 1;
@@ -537,22 +575,22 @@
{
IdAssigner idAssigner;
- if (!idAssigner.consume(&mContext, &mergedTable)) {
+ if (!idAssigner.consume(&mContext, &mFinalTable)) {
mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
return 1;
}
}
- mContext.mNameMangler = util::make_unique<NameMangler>(
- NameManglerPolicy{ mContext.mCompilationPackage, tableMerger.getMergedPackages() });
+ mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
+ mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
mContext.mSymbols = JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mergedTable))
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
.addSymbolTable(std::move(mContext.mSymbols))
.build();
{
ReferenceLinker linker;
- if (!linker.consume(&mContext, &mergedTable)) {
+ if (!linker.consume(&mContext, &mFinalTable)) {
mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
return 1;
}
@@ -566,6 +604,7 @@
return 1;
}
+ bool error = false;
{
ManifestFixerOptions manifestFixerOptions;
manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
@@ -575,6 +614,11 @@
error = true;
}
+ // AndroidManifest.xml has no resource name, but the CallSite is built from the name
+ // (aka, which package the AndroidManifest.xml is coming from).
+ // So we give it a package name so it can see local resources.
+ manifestXml->file.name.package = mContext.getCompilationPackage().toString();
+
XmlReferenceLinker manifestLinker;
if (manifestLinker.consume(&mContext, manifestXml.get())) {
if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
@@ -598,20 +642,26 @@
}
}
- for (; !filesToProcess.empty(); filesToProcess.pop()) {
- FilesToProcess& f = filesToProcess.front();
- if (f.file.name.type != ResourceType::kRaw &&
- util::stringEndsWith<char>(f.source.path, ".xml.flat")) {
+ if (error) {
+ mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
+ return 1;
+ }
+
+ for (const FileToProcess& file : mFilesToProcess) {
+ if (file.file.name.type != ResourceType::kRaw &&
+ util::stringEndsWith<char>(file.source.path, ".xml.flat")) {
if (mOptions.verbose) {
- mContext.getDiagnostics()->note(DiagMessage() << "linking " << f.source.path);
+ mContext.getDiagnostics()->note(DiagMessage()
+ << "linking " << file.source.path);
}
- std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(f.source.path);
+ std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport(
+ file.source.path);
if (!xmlRes) {
return 1;
}
- xmlRes->file = std::move(f.file);
+ xmlRes->file = std::move(file.file);
XmlReferenceLinker xmlLinker;
if (xmlLinker.consume(&mContext, xmlRes.get())) {
@@ -631,7 +681,7 @@
}
if (!mOptions.noAutoVersion) {
- Maybe<ResourceTable::SearchResult> result = mergedTable.findResource(
+ Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
xmlRes->file.name);
for (int sdkLevel : xmlLinker.getSdkLevels()) {
if (sdkLevel > xmlRes->file.config.sdkVersion &&
@@ -639,7 +689,7 @@
xmlRes->file.config,
sdkLevel)) {
xmlRes->file.config.sdkVersion = sdkLevel;
- if (!mergedTable.addFileReference(xmlRes->file.name,
+ if (!mFinalTable.addFileReference(xmlRes->file.name,
xmlRes->file.config,
xmlRes->file.source,
util::utf8ToUtf16(
@@ -662,10 +712,11 @@
}
} else {
if (mOptions.verbose) {
- mContext.getDiagnostics()->note(DiagMessage() << "copying " << f.source.path);
+ mContext.getDiagnostics()->note(DiagMessage() << "copying "
+ << file.source.path);
}
- if (!copyFileToArchive(f.source.path, buildResourceFileName(f.file), 0,
+ if (!copyFileToArchive(file.source.path, buildResourceFileName(file.file), 0,
archiveWriter.get())) {
error = true;
}
@@ -679,13 +730,13 @@
if (!mOptions.noAutoVersion) {
AutoVersioner versioner;
- if (!versioner.consume(&mContext, &mergedTable)) {
+ if (!versioner.consume(&mContext, &mFinalTable)) {
mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
return 1;
}
}
- if (!flattenTable(&mergedTable, archiveWriter.get())) {
+ if (!flattenTable(&mFinalTable, archiveWriter.get())) {
mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
return 1;
}
@@ -704,7 +755,7 @@
// to the original package, and private and public symbols to the private package.
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(),
+ if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
mContext.getCompilationPackage(), options)) {
return 1;
}
@@ -713,12 +764,12 @@
outputPackage = mOptions.privateSymbols.value();
}
- if (!writeJavaFile(&mergedTable, actualPackage, outputPackage, options)) {
+ if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
return 1;
}
- for (std::string& extraPackage : mOptions.extraJavaPackages) {
- if (!writeJavaFile(&mergedTable, actualPackage, util::utf8ToUtf16(extraPackage),
+ for (const std::string& extraPackage : mOptions.extraJavaPackages) {
+ if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
options)) {
return 1;
}
@@ -732,10 +783,10 @@
}
if (mOptions.verbose) {
- Debug::printTable(&mergedTable);
- for (; !tableMerger.getFileMergeQueue()->empty();
- tableMerger.getFileMergeQueue()->pop()) {
- const FileToMerge& f = tableMerger.getFileMergeQueue()->front();
+ Debug::printTable(&mFinalTable);
+ for (; !mTableMerger->getFileMergeQueue()->empty();
+ mTableMerger->getFileMergeQueue()->pop()) {
+ const FileToMerge& f = mTableMerger->getFileMergeQueue()->front();
mContext.getDiagnostics()->note(
DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
<< std::hex << (uintptr_t) f.srcTable << std::dec);
@@ -744,17 +795,40 @@
return 0;
}
+
+private:
+ LinkOptions mOptions;
+ LinkContext mContext;
+ ResourceTable mFinalTable;
+ std::unique_ptr<TableMerger> mTableMerger;
+
+ struct FileToProcess {
+ ResourceFile file;
+ Source source;
+ };
+
+ struct FileToProcessComparator {
+ bool operator()(const FileToProcess& a, const FileToProcess& b) {
+ return std::tie(a.file.name, a.file.config) < std::tie(b.file.name, b.file.config);
+ }
+ };
+
+ std::set<FileToProcess, FileToProcessComparator> mFilesToProcess;
};
int link(const std::vector<StringPiece>& args) {
LinkOptions options;
Maybe<std::string> privateSymbolsPackage;
Maybe<std::string> minSdkVersion, targetSdkVersion;
+ std::vector<std::string> extraJavaPackages;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
.requiredFlag("--manifest", "Path to the Android manifest to build",
&options.manifestPath)
.optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
+ .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
+ "The last conflicting resource given takes precedence.",
+ &options.overlayFiles)
.optionalFlag("--java", "Directory in which to generate R.java",
&options.generateJavaClassPath)
.optionalFlag("--proguard", "Output file for generated Proguard rules",
@@ -775,7 +849,7 @@
"If not specified, public and private symbols will use the application's "
"package name", &privateSymbolsPackage)
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
- "package names", &options.extraJavaPackages)
+ "package names", &extraJavaPackages)
.optionalSwitch("-v", "Enables verbose logging", &options.verbose);
if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -794,7 +868,15 @@
options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
}
- LinkCommand cmd = { options };
+ // Populate the set of extra packages for which to generate R.java.
+ for (std::string& extraPackage : extraJavaPackages) {
+ // A given package can actually be a colon separated list of packages.
+ for (StringPiece package : util::split(extraPackage, ':')) {
+ options.extraJavaPackages.insert(package.toString());
+ }
+ }
+
+ LinkCommand cmd(options);
return cmd.run(flags.getArgs());
}
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 7b3fc35..4d3a483 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -17,7 +17,9 @@
#ifndef AAPT_LINKER_LINKERS_H
#define AAPT_LINKER_LINKERS_H
+#include "Resource.h"
#include "process/IResourceTableConsumer.h"
+#include "xml/XmlDom.h"
#include <set>
@@ -28,6 +30,14 @@
struct ConfigDescription;
/**
+ * Defines the location in which a value exists. This determines visibility of other
+ * package's private symbols.
+ */
+struct CallSite {
+ ResourceNameRef resource;
+};
+
+/**
* Determines whether a versioned resource should be created. If a versioned resource already
* exists, it takes precedence.
*/
@@ -39,7 +49,7 @@
};
struct XmlAutoVersioner : public IXmlResourceConsumer {
- bool consume(IAaptContext* context, XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};
/**
@@ -69,15 +79,6 @@
};
/**
- * Resolves all references to resources in the ResourceTable and assigns them IDs.
- * The ResourceTable must already have IDs assigned to each resource.
- * Once the ResourceTable is processed by this linker, it is ready to be flattened.
- */
-struct ReferenceLinker : public IResourceTableConsumer {
- bool consume(IAaptContext* context, ResourceTable* table) override;
-};
-
-/**
* Resolves attributes in the XmlResource and compiles string values to resource values.
* Once an XmlResource is processed by this linker, it is ready to be flattened.
*/
@@ -86,7 +87,7 @@
std::set<int> mSdkLevelsFound;
public:
- bool consume(IAaptContext* context, XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
/**
* Once the XmlResource has been consumed, this returns the various SDK levels in which
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 52d9426..2034c57 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -15,10 +15,9 @@
*/
#include "ResourceUtils.h"
-#include "XmlDom.h"
-
#include "link/ManifestFixer.h"
#include "util/Util.h"
+#include "xml/XmlDom.h"
namespace aapt {
@@ -63,7 +62,7 @@
return true;
}
-bool ManifestFixer::consume(IAaptContext* context, XmlResource* doc) {
+bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
xml::Element* root = xml::findRootElement(doc->root.get());
if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
context->getDiagnostics()->error(DiagMessage(doc->file.source)
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 16e161d..a77e6d5 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -18,6 +18,10 @@
#define AAPT_LINK_MANIFESTFIXER_H
#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
+#include "xml/XmlDom.h"
+
+#include <string>
namespace aapt {
@@ -36,7 +40,7 @@
ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
}
- bool consume(IAaptContext* context, XmlResource* doc) override;
+ bool consume(IAaptContext* context, xml::XmlResource* doc) override;
};
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 5c5d8af..f6bf895 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -15,7 +15,6 @@
*/
#include "link/ManifestFixer.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -51,13 +50,13 @@
.build();
}
- std::unique_ptr<XmlResource> verify(const StringPiece& str) {
+ std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) {
return verifyWithOptions(str, {});
}
- std::unique_ptr<XmlResource> verifyWithOptions(const StringPiece& str,
- const ManifestFixerOptions& options) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(str);
+ std::unique_ptr<xml::XmlResource> verifyWithOptions(const StringPiece& str,
+ const ManifestFixerOptions& options) {
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str);
ManifestFixer fixer(options);
if (fixer.consume(mContext.get(), doc.get())) {
return doc;
@@ -88,7 +87,7 @@
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
- std::unique_ptr<XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index 5a2f5f0..3c8af4f 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -15,7 +15,6 @@
*/
#include "ResourceTable.h"
-
#include "link/Linkers.h"
#include <algorithm>
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8c924b5..2743539 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -14,17 +14,18 @@
* limitations under the License.
*/
+#include "ReferenceLinker.h"
+
#include "Diagnostics.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "util/Util.h"
#include "ValueVisitor.h"
-
#include "link/Linkers.h"
-#include "link/ReferenceLinkerVisitor.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "util/Util.h"
+#include "xml/XmlUtil.h"
#include <androidfw/ResourceTypes.h>
#include <cassert>
@@ -41,52 +42,15 @@
*
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
*/
-class StyleAndReferenceLinkerVisitor : public ValueVisitor {
+class ReferenceLinkerVisitor : public ValueVisitor {
private:
- ReferenceLinkerVisitor mReferenceVisitor;
IAaptContext* mContext;
ISymbolTable* mSymbols;
- IPackageDeclStack* mPackageDecls;
+ xml::IPackageDeclStack* mPackageDecls;
StringPool* mStringPool;
+ CallSite* mCallSite;
bool mError = false;
- const ISymbolTable::Symbol* findAttributeSymbol(Reference* reference) {
- assert(reference);
- assert(reference->name || reference->id);
-
- if (reference->name) {
- // Transform the package name if it is an alias.
- Maybe<ResourceName> realName = mPackageDecls->transformPackage(
- reference->name.value(), mContext->getCompilationPackage());
-
- // Mangle the reference name if it should be mangled.
- Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
- realName ? realName.value() : reference->name.value());
-
- const ISymbolTable::Symbol* s = nullptr;
- if (mangledName) {
- s = mSymbols->findByName(mangledName.value());
- } else if (realName) {
- s = mSymbols->findByName(realName.value());
- } else {
- s = mSymbols->findByName(reference->name.value());
- }
-
- if (s && s->attribute) {
- return s;
- }
- }
-
- if (reference->id) {
- if (const ISymbolTable::Symbol* s = mSymbols->findById(reference->id.value())) {
- if (s->attribute) {
- return s;
- }
- }
- }
- return nullptr;
- }
-
/**
* Transform a RawString value into a more specific, appropriate value, based on the
* Attribute. If a non RawString value is passed in, this is an identity transform.
@@ -94,8 +58,8 @@
std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
const Attribute* attr) {
if (RawString* rawString = valueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed = ResourceUtils::parseItemForAttribute(
- *rawString->value, attr);
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::parseItemForAttribute(*rawString->value, attr);
// If we could not parse as any specific type, try a basic STRING.
if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
@@ -114,63 +78,19 @@
return value;
}
- void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
- const Item* value) {
- *msg << "expected";
- if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- *msg << " boolean";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
- *msg << " color";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
- *msg << " dimension";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
- *msg << " enum";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
- *msg << " flags";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
- *msg << " float";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
- *msg << " fraction";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
- *msg << " integer";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
- *msg << " reference";
- }
-
- if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
- *msg << " string";
- }
-
- *msg << " but got " << *value;
- }
-
public:
using ValueVisitor::visit;
- StyleAndReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
- StringPool* stringPool, IPackageDeclStack* decl) :
- mReferenceVisitor(context, symbols, decl), mContext(context), mSymbols(symbols),
- mPackageDecls(decl), mStringPool(stringPool) {
+ ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
+ xml::IPackageDeclStack* decl,CallSite* callSite) :
+ mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
+ mCallSite(callSite) {
}
- void visit(Reference* reference) override {
- mReferenceVisitor.visit(reference);
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
+ mError = true;
+ }
}
/**
@@ -184,58 +104,190 @@
}
for (Style::Entry& entry : style->entries) {
- if (const ISymbolTable::Symbol* s = findAttributeSymbol(&entry.key)) {
+ std::string errStr;
+
+ // Transform the attribute reference so that it is using the fully qualified package
+ // name. This will also mark the reference as being able to see private resources if
+ // there was a '*' in the reference or if the package came from the private namespace.
+ Reference transformedReference = entry.key;
+ transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
+ &transformedReference);
+
+ // Find the attribute in the symbol table and check if it is visible from this callsite.
+ const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
+ transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
+ if (symbol) {
// Assign our style key the correct ID.
- entry.key.id = s->id;
+ entry.key.id = symbol->id;
// Try to convert the value to a more specific, typed value based on the
// attribute it is set to.
- entry.value = parseValueWithAttribute(std::move(entry.value), s->attribute.get());
+ entry.value = parseValueWithAttribute(std::move(entry.value),
+ symbol->attribute.get());
// Link/resolve the final value (mostly if it's a reference).
entry.value->accept(this);
// Now verify that the type of this item is compatible with the attribute it
- // is defined for.
- android::Res_value val = {};
- entry.value->flatten(&val);
-
- // Always allow references.
- const uint32_t typeMask = s->attribute->typeMask |
- android::ResTable_map::TYPE_REFERENCE;
-
- if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
+ // is defined for. We pass `nullptr` as the DiagMessage so that this check is
+ // fast and we avoid creating a DiagMessage when the match is successful.
+ if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
// The actual type of this item is incompatible with the attribute.
- DiagMessage msg(style->getSource());
- buildAttributeMismatchMessage(&msg, s->attribute.get(), entry.value.get());
+ DiagMessage msg(entry.key.getSource());
+
+ // Call the matches method again, this time with a DiagMessage so we fill
+ // in the actual error message.
+ symbol->attribute->matches(entry.value.get(), &msg);
mContext->getDiagnostics()->error(msg);
mError = true;
}
+
} else {
- DiagMessage msg(style->getSource());
+ DiagMessage msg(entry.key.getSource());
msg << "style attribute '";
- if (entry.key.name) {
- msg << entry.key.name.value().package << ":" << entry.key.name.value().entry;
- } else {
- msg << entry.key.id.value();
- }
- msg << "' not found";
+ ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
+ msg << "' " << errStr;
mContext->getDiagnostics()->error(msg);
mError = true;
}
}
}
- inline bool hasError() {
- return mError || mReferenceVisitor.hasError();
+ bool hasError() {
+ return mError;
}
};
-struct EmptyDeclStack : public IPackageDeclStack {
- Maybe<ResourceName> transformPackage(const ResourceName& name,
- const StringPiece16& localPackage) const override {
- if (name.package.empty()) {
- return ResourceName{ localPackage.toString(), name.type, name.entry };
+} // namespace
+
+/**
+ * The symbol is visible if it is public, or if the reference to it is requesting private access
+ * or if the callsite comes from the same package.
+ */
+bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
+ const CallSite& callSite) {
+ if (!symbol.isPublic && !ref.privateReference) {
+ if (ref.name) {
+ return callSite.resource.package == ref.name.value().package;
+ } else if (ref.id) {
+ return ref.id.value().packageId() == symbol.id.packageId();
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
+ NameMangler* mangler,
+ ISymbolTable* symbols) {
+ if (reference.name) {
+ Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
+ return symbols->findByName(mangled ? mangled.value() : reference.name.value());
+ } else if (reference.id) {
+ return symbols->findById(reference.id.value());
+ } else {
+ return nullptr;
+ }
+}
+
+const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
+ CallSite* callSite, std::string* outError) {
+ const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
+ if (!symbol) {
+ if (outError) *outError = "not found";
+ return nullptr;
+ }
+
+ if (!isSymbolVisible(*symbol, reference, *callSite)) {
+ if (outError) *outError = "is private";
+ return nullptr;
+ }
+ return symbol;
+}
+
+const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
+ CallSite* callSite, std::string* outError) {
+ const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
+ symbols, callSite,
+ outError);
+ if (!symbol) {
+ return nullptr;
+ }
+
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ return nullptr;
+ }
+ return symbol;
+}
+
+Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
+ NameMangler* nameMangler,
+ ISymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError) {
+ const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
+ if (!symbol) {
+ return {};
+ }
+
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ return {};
+ }
+ return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+}
+
+void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
+ const Reference& transformed) {
+ assert(outMsg);
+
+ if (orig.name) {
+ *outMsg << orig.name.value();
+ if (transformed.name.value() != orig.name.value()) {
+ *outMsg << " (aka " << transformed.name.value() << ")";
+ }
+ } else {
+ *outMsg << orig.id.value();
+ }
+}
+
+bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
+ ISymbolTable* symbols, xml::IPackageDeclStack* decls,
+ CallSite* callSite) {
+ assert(reference);
+ assert(reference->name || reference->id);
+
+ Reference transformedReference = *reference;
+ transformReferenceFromNamespace(decls, context->getCompilationPackage(),
+ &transformedReference);
+
+ std::string errStr;
+ const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
+ transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
+ if (s) {
+ reference->id = s->id;
+ return true;
+ }
+
+ DiagMessage errorMsg(reference->getSource());
+ errorMsg << "resource ";
+ writeResourceName(&errorMsg, *reference, transformedReference);
+ errorMsg << " " << errStr;
+ context->getDiagnostics()->error(errorMsg);
+ return false;
+}
+
+namespace {
+
+struct EmptyDeclStack : public xml::IPackageDeclStack {
+ Maybe<xml::ExtractedPackage> transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
}
return {};
}
@@ -259,14 +311,16 @@
error = true;
}
+ CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
+ ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
+ &table->stringPool, &declStack, &callSite);
+
for (auto& configValue : entry->values) {
- StyleAndReferenceLinkerVisitor visitor(context,
- context->getExternalSymbols(),
- &table->stringPool, &declStack);
configValue.value->accept(&visitor);
- if (visitor.hasError()) {
- error = true;
- }
+ }
+
+ if (visitor.hasError()) {
+ error = true;
}
}
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
new file mode 100644
index 0000000..a0eb00c
--- /dev/null
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LINKER_REFERENCELINKER_H
+#define AAPT_LINKER_REFERENCELINKER_H
+
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "link/Linkers.h"
+#include "process/IResourceTableConsumer.h"
+#include "process/SymbolTable.h"
+#include "xml/XmlDom.h"
+
+#include <cassert>
+
+namespace aapt {
+
+/**
+ * Resolves all references to resources in the ResourceTable and assigns them IDs.
+ * The ResourceTable must already have IDs assigned to each resource.
+ * Once the ResourceTable is processed by this linker, it is ready to be flattened.
+ */
+struct ReferenceLinker : public IResourceTableConsumer {
+ /**
+ * Returns true if the symbol is visible by the reference and from the callsite.
+ */
+ static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
+ const CallSite& callSite);
+
+ /**
+ * Performs name mangling and looks up the resource in the symbol table. Returns nullptr
+ * if the symbol was not found.
+ */
+ static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference,
+ NameMangler* mangler, ISymbolTable* symbols);
+
+ /**
+ * Performs name mangling and looks up the resource in the symbol table. If the symbol is
+ * not visible by the reference at the callsite, nullptr is returned. outError holds
+ * the error message.
+ */
+ static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
+ NameMangler* nameMangler,
+ ISymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError);
+
+ /**
+ * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
+ * That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
+ */
+ static const ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
+ NameMangler* nameMangler,
+ ISymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError);
+
+ /**
+ * Resolves the attribute reference and returns an xml::AaptAttribute if successful.
+ * If resolution fails, outError holds the error message.
+ */
+ static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
+ NameMangler* nameMangler,
+ ISymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError);
+
+ /**
+ * Writes the resource name to the DiagMessage, using the "orig_name (aka <transformed_name>)"
+ * syntax.
+ */
+ static void writeResourceName(DiagMessage* outMsg, const Reference& orig,
+ const Reference& transformed);
+
+ /**
+ * Transforms the package name of the reference to the fully qualified package name using
+ * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
+ * to the reference at the callsite, the reference is updated with an ID.
+ * Returns false on failure, and an error message is logged to the IDiagnostics in the context.
+ */
+ static bool linkReference(Reference* reference, IAaptContext* context, ISymbolTable* symbols,
+ xml::IPackageDeclStack* decls, CallSite* callSite);
+
+ /**
+ * Links all references in the ResourceTable.
+ */
+ bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_LINKER_REFERENCELINKER_H */
diff --git a/tools/aapt2/link/ReferenceLinkerVisitor.h b/tools/aapt2/link/ReferenceLinkerVisitor.h
deleted file mode 100644
index c70531b..0000000
--- a/tools/aapt2/link/ReferenceLinkerVisitor.h
+++ /dev/null
@@ -1,109 +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.
- */
-
-#ifndef AAPT_LINKER_REFERENCELINKERVISITOR_H
-#define AAPT_LINKER_REFERENCELINKERVISITOR_H
-
-#include "Resource.h"
-#include "ResourceValues.h"
-#include "ValueVisitor.h"
-
-#include "process/IResourceTableConsumer.h"
-#include "process/SymbolTable.h"
-
-#include <cassert>
-
-namespace aapt {
-
-/**
- * The ReferenceLinkerVisitor will follow all references and make sure they point
- * to resources that actually exist in the given ISymbolTable.
- * Once the target resource has been found, the ID of the resource will be assigned
- * to the reference object.
- */
-class ReferenceLinkerVisitor : public ValueVisitor {
- using ValueVisitor::visit;
-private:
- IAaptContext* mContext;
- ISymbolTable* mSymbols;
- IPackageDeclStack* mPackageDecls;
- bool mError = false;
-
-public:
- ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, IPackageDeclStack* decls) :
- mContext(context), mSymbols(symbols), mPackageDecls(decls) {
- }
-
- /**
- * Lookup a reference and ensure it exists, either in our local table, or as an external
- * symbol. Once found, assign the ID of the target resource to this reference object.
- */
- void visit(Reference* reference) override {
- assert(reference);
- assert(reference->name || reference->id);
-
- // We prefer to lookup by name if the name is set. Otherwise it could be
- // an out-of-date ID.
- if (reference->name) {
- // Transform the package name if it is an alias.
- Maybe<ResourceName> realName = mPackageDecls->transformPackage(
- reference->name.value(), mContext->getCompilationPackage());
-
- // Mangle the reference name if it should be mangled.
- Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
- realName ? realName.value() : reference->name.value());
-
- const ISymbolTable::Symbol* s = nullptr;
- if (mangledName) {
- s = mSymbols->findByName(mangledName.value());
- } else if (realName) {
- s = mSymbols->findByName(realName.value());
- } else {
- s = mSymbols->findByName(reference->name.value());
- }
-
- if (s) {
- reference->id = s->id;
- return;
- }
-
- DiagMessage errorMsg;
- errorMsg << "reference to " << reference->name.value();
- if (realName) {
- errorMsg << " (aka " << realName.value() << ")";
- }
- errorMsg << " was not found";
- mContext->getDiagnostics()->error(errorMsg);
- mError = true;
- return;
- }
-
- if (!mSymbols->findById(reference->id.value())) {
- mContext->getDiagnostics()->error(DiagMessage()
- << "reference to " << reference->id.value()
- << " was not found");
- mError = true;
- }
- }
-
- inline bool hasError() {
- return mError;
- }
-};
-
-} // namespace aapt
-
-#endif /* AAPT_LINKER_REFERENCELINKERVISITOR_H */
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 5e7641a..8d324fe 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "link/Linkers.h"
+#include "link/ReferenceLinker.h"
#include "process/SymbolTable.h"
#include "test/Builders.h"
@@ -44,7 +44,7 @@
.setSymbolTable(JoinedSymbolTableBuilder()
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
.addSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@android:string/ok", ResourceId(0x01040034))
+ .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
.build())
.build())
.build();
@@ -92,12 +92,12 @@
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
.setSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
- .addSymbol(u"@android:attr/foo", ResourceId(0x01010001),
+ .addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
+ .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_COLOR)
.build())
- .addSymbol(u"@android:attr/bar", ResourceId(0x01010002),
+ .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_FLAGS)
.addItem(u"one", 0x01)
@@ -132,7 +132,7 @@
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
.setSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@com.app.test:attr/com.android.support$foo",
+ .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
ResourceId(0x7f010000), test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
.build())
@@ -156,4 +156,78 @@
EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
}
+TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000),
+ u"@android:string/hidden")
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
+ .setSymbolTable(JoinedSymbolTableBuilder()
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
+ .addSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
+ .build())
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
+}
+
+TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000),
+ u"@com.app.lib:string/hidden")
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } })
+ .setSymbolTable(JoinedSymbolTableBuilder()
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
+ .addSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@com.app.test:string/com.app.lib$hidden",
+ ResourceId(0x7f040034))
+ .build())
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
+}
+
+TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.test", 0x7f)
+ .addValue(u"@com.app.test:style/Theme", test::StyleBuilder()
+ .addItem(u"@android:attr/hidden", ResourceUtils::tryParseColor(u"#ff00ff"))
+ .build())
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage(u"com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
+ .setSymbolTable(JoinedSymbolTableBuilder()
+ .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
+ .addSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR)
+ .build())
+ .build())
+ .build())
+ .build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1eea410..a06a1bf 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -37,7 +37,7 @@
/**
* This will merge packages with the same package name (or no package name).
*/
-bool TableMerger::merge(const Source& src, ResourceTable* table) {
+bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) {
const uint8_t desiredPackageId = mContext->getPackageId();
bool error = false;
@@ -55,7 +55,7 @@
// mangled, then looked up at resolution time.
// Also, when linking, we convert references with no package name to use
// the compilation package name.
- if (!doMerge(src, table, package.get(), false)) {
+ if (!doMerge(src, table, package.get(), false, overrideExisting)) {
error = true;
}
}
@@ -79,7 +79,7 @@
bool mangle = packageName != mContext->getCompilationPackage();
mMergedPackages.insert(package->name);
- if (!doMerge(src, table, package.get(), mangle)) {
+ if (!doMerge(src, table, package.get(), mangle, false)) {
error = true;
}
}
@@ -87,7 +87,8 @@
}
bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
- ResourceTablePackage* srcPackage, const bool manglePackage) {
+ ResourceTablePackage* srcPackage, const bool manglePackage,
+ const bool overrideExisting) {
bool error = false;
for (auto& srcType : srcPackage->types) {
@@ -149,7 +150,7 @@
if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
const int collisionResult = ResourceTable::resolveValueCollision(
iter->value.get(), srcValue.value.get());
- if (collisionResult == 0) {
+ if (collisionResult == 0 && !overrideExisting) {
// Error!
ResourceNameRef resourceName(srcPackage->name,
srcType->type,
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index c903f1b..a2c9dbf 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -63,7 +63,7 @@
/**
* Merges resources from the same or empty package. This is for local sources.
*/
- bool merge(const Source& src, ResourceTable* table);
+ bool merge(const Source& src, ResourceTable* table, bool overrideExisting);
/**
* Merges resources from the given package, mangling the name. This is for static libraries.
@@ -79,7 +79,7 @@
std::queue<FileToMerge> mFilesToMerge;
bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
- const bool manglePackage);
+ const bool manglePackage, const bool overrideExisting);
std::unique_ptr<Value> cloneAndMangle(ResourceTable* table, const std::u16string& package,
Value* value);
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 0af4314..b7ffba7 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -59,7 +59,7 @@
ResourceTable finalTable;
TableMerger merger(mContext.get(), &finalTable);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get(), false));
ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
@@ -89,7 +89,7 @@
ResourceTable finalTable;
TableMerger merger(mContext.get(), &finalTable);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get(), false));
ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 147b9bf..a26d763 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -17,49 +17,91 @@
#include "Diagnostics.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
-#include "XmlDom.h"
-
#include "link/Linkers.h"
-#include "link/ReferenceLinkerVisitor.h"
+#include "link/ReferenceLinker.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
#include "util/Util.h"
+#include "xml/XmlDom.h"
namespace aapt {
namespace {
-class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor {
+/**
+ * Visits all references (including parents of styles, references in styles, arrays, etc) and
+ * links their symbolic name to their Resource ID, performing mangling and package aliasing
+ * as needed.
+ */
+class ReferenceVisitor : public ValueVisitor {
private:
IAaptContext* mContext;
ISymbolTable* mSymbols;
+ xml::IPackageDeclStack* mDecls;
+ CallSite* mCallSite;
+ bool mError;
+
+public:
+ using ValueVisitor::visit;
+
+ ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls,
+ CallSite* callSite) :
+ mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
+ mError(false) {
+ }
+
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
+ mError = true;
+ }
+ }
+
+ bool hasError() const {
+ return mError;
+ }
+};
+
+/**
+ * Visits each xml Element and compiles the attributes within.
+ */
+class XmlVisitor : public xml::PackageAwareVisitor {
+private:
+ IAaptContext* mContext;
+ ISymbolTable* mSymbols;
+ Source mSource;
std::set<int>* mSdkLevelsFound;
- ReferenceLinkerVisitor mReferenceLinkerVisitor;
+ CallSite* mCallSite;
+ ReferenceVisitor mReferenceVisitor;
bool mError = false;
public:
using xml::PackageAwareVisitor::visit;
- XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
- std::set<int>* sdkLevelsFound) :
- mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound),
- mReferenceLinkerVisitor(context, symbols, this) {
+ XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
+ std::set<int>* sdkLevelsFound, CallSite* callSite) :
+ mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
+ mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
}
void visit(xml::Element* el) override {
+ const Source source = mSource.withLine(el->lineNumber);
for (xml::Attribute& attr : el->attributes) {
- Maybe<std::u16string> maybePackage =
- util::extractPackageFromNamespace(attr.namespaceUri);
+ Maybe<xml::ExtractedPackage> maybePackage =
+ xml::extractPackageFromNamespace(attr.namespaceUri);
if (maybePackage) {
// There is a valid package name for this attribute. We will look this up.
- StringPiece16 package = maybePackage.value();
+ StringPiece16 package = maybePackage.value().package;
if (package.empty()) {
// Empty package means the 'current' or 'local' package.
package = mContext->getCompilationPackage();
}
- attr.compiledAttribute = compileAttribute(
- ResourceName{ package.toString(), ResourceType::kAttr, attr.name });
+ Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
+ attrRef.privateReference = maybePackage.value().privateNamespace;
+
+ std::string errStr;
+ attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
+ attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
// Convert the string value into a compiled Value if this is a valid attribute.
if (attr.compiledAttribute) {
@@ -76,15 +118,16 @@
!(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
// We won't be able to encode this as a string.
mContext->getDiagnostics()->error(
- DiagMessage() << "'" << attr.value << "' "
- << "is incompatible with attribute "
- << package << ":" << attr.name << " " << *attribute);
+ DiagMessage(source) << "'" << attr.value << "' "
+ << "is incompatible with attribute "
+ << package << ":" << attr.name << " "
+ << *attribute);
mError = true;
}
} else {
- mContext->getDiagnostics()->error(
- DiagMessage() << "attribute '" << package << ":" << attr.name
- << "' was not found");
+ mContext->getDiagnostics()->error(DiagMessage(source)
+ << "attribute '" << package << ":"
+ << attr.name << "' " << errStr);
mError = true;
}
@@ -95,7 +138,8 @@
if (attr.compiledValue) {
// With a compiledValue, we must resolve the reference and assign it an ID.
- attr.compiledValue->accept(&mReferenceLinkerVisitor);
+ attr.compiledValue->setSource(source);
+ attr.compiledValue->accept(&mReferenceVisitor);
}
}
@@ -103,27 +147,18 @@
xml::PackageAwareVisitor::visit(el);
}
- Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) {
- Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name);
- if (const ISymbolTable::Symbol* symbol = mSymbols->findByName(
- mangledName ? mangledName.value() : name)) {
- if (symbol->attribute) {
- return xml::AaptAttribute{ symbol->id, *symbol->attribute };
- }
- }
- return {};
- }
-
- inline bool hasError() {
- return mError || mReferenceLinkerVisitor.hasError();
+ bool hasError() {
+ return mError || mReferenceVisitor.hasError();
}
};
} // namespace
-bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) {
+bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
mSdkLevelsFound.clear();
- XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound);
+ CallSite callSite = { resource->file.name };
+ XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
+ &mSdkLevelsFound, &callSite);
if (resource->root) {
resource->root->accept(&visitor);
return !visitor.hasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 7f91ec3..3bfaf91 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -31,37 +31,40 @@
.setNameManglerPolicy(
NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
.setSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
+ .addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_ENUM |
android::ResTable_map::TYPE_DIMENSION)
.addItem(u"match_parent", 0xffffffff)
.build())
- .addSymbol(u"@android:attr/background", ResourceId(0x01010001),
+ .addPublicSymbol(u"@android:attr/background", ResourceId(0x01010001),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addSymbol(u"@android:attr/attr", ResourceId(0x01010002),
+ .addPublicSymbol(u"@android:attr/attr", ResourceId(0x01010002),
test::AttributeBuilder().build())
- .addSymbol(u"@android:attr/text", ResourceId(0x01010003),
+ .addPublicSymbol(u"@android:attr/text", ResourceId(0x01010003),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_STRING)
.build())
// Add one real symbol that was introduces in v21
- .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
+ .addPublicSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435),
test::AttributeBuilder().build())
- .addSymbol(u"@android:id/id", ResourceId(0x01030000))
+ // Private symbol.
+ .addSymbol(u"@android:color/hidden", ResourceId(0x01020001))
+
+ .addPublicSymbol(u"@android:id/id", ResourceId(0x01030000))
.addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000))
.addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000))
.addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001))
.addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
+ .addPublicSymbol(u"@com.app.test:attr/com.android.support$colorAccent",
ResourceId(0x7f010001), test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
+ .addPublicSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002),
test::AttributeBuilder().build())
.build())
.build();
@@ -71,23 +74,8 @@
std::unique_ptr<IAaptContext> mContext;
};
-static xml::Element* getRootElement(XmlResource* doc) {
- xml::Node* node = doc->root.get();
- while (xml::nodeCast<xml::Namespace>(node)) {
- if (node->children.empty()) {
- return nullptr;
- }
- node = node->children.front().get();
- }
-
- if (xml::Element* el = xml::nodeCast<xml::Element>(node)) {
- return el;
- }
- return nullptr;
-}
-
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/green"
@@ -97,7 +85,7 @@
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = getRootElement(doc.get());
+ xml::Element* viewEl = xml::findRootElement(doc.get());
ASSERT_NE(viewEl, nullptr);
xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android",
@@ -132,8 +120,26 @@
ASSERT_EQ(xmlAttr->compiledValue, nullptr);
}
+TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:colorAccent="@android:color/hidden" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
+}
+
+TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:colorAccent="@*android:color/hidden" />)EOF");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+}
+
TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="#ffffff" />)EOF");
@@ -143,14 +149,14 @@
}
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)EOF");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = getRootElement(doc.get());
+ xml::Element* viewEl = xml::findRootElement(doc.get());
ASSERT_NE(viewEl, nullptr);
xml::Attribute* xmlAttr = viewEl->findAttribute(
@@ -162,14 +168,14 @@
}
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)EOF");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = getRootElement(doc.get());
+ xml::Element* viewEl = xml::findRootElement(doc.get());
ASSERT_NE(viewEl, nullptr);
xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto",
@@ -185,7 +191,7 @@
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res/android"
app:attr="@app:id/id">
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
@@ -195,7 +201,7 @@
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = getRootElement(doc.get());
+ xml::Element* viewEl = xml::findRootElement(doc.get());
ASSERT_NE(viewEl, nullptr);
// All attributes and references in this element should be referring to "android" (0x01).
@@ -225,14 +231,14 @@
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
- std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)EOF");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = getRootElement(doc.get());
+ xml::Element* viewEl = xml::findRootElement(doc.get());
ASSERT_NE(viewEl, nullptr);
// All attributes and references in this element should be referring to "com.app.test" (0x7f).
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 24ad05d..a2528d2 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -49,25 +49,13 @@
};
namespace xml {
-struct Node;
+struct XmlResource;
}
-struct XmlResource {
- ResourceFile file;
- std::unique_ptr<xml::Node> root;
-};
-
struct IXmlResourceConsumer {
virtual ~IXmlResourceConsumer() = default;
- virtual bool consume(IAaptContext* context, XmlResource* resource) = 0;
-};
-
-struct IPackageDeclStack {
- virtual ~IPackageDeclStack() = default;
-
- virtual Maybe<ResourceName> transformPackage(const ResourceName& name,
- const StringPiece16& localPackage) const = 0;
+ virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0;
};
} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 9a8b263..6ad2f9c 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -51,6 +51,7 @@
std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
+ symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
@@ -60,7 +61,7 @@
if (iter != sr.entry->values.end() && iter->config == kDefaultConfig) {
// This resource has an Attribute.
if (Attribute* attr = valueCast<Attribute>(iter->value.get())) {
- symbol->attribute = std::unique_ptr<Attribute>(attr->clone(nullptr));
+ symbol->attribute = util::make_unique<Attribute>(*attr);
} else {
return {};
}
@@ -76,17 +77,8 @@
return symbol.get();
}
-
-static std::shared_ptr<ISymbolTable::Symbol> lookupIdInTable(const android::ResTable& table,
- ResourceId id) {
- android::Res_value val = {};
- ssize_t block = table.getResource(id.id, &val, true);
- if (block >= 0) {
- std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
- s->id = id;
- return s;
- }
-
+static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
+ ResourceId id) {
// Try as a bag.
const android::ResTable::bag_entry* entry;
ssize_t count = table.lockBag(id.id, &entry);
@@ -110,29 +102,40 @@
if (s->attribute) {
for (size_t i = 0; i < (size_t) count; i++) {
- if (!Res_INTERNALID(entry[i].map.name.ident)) {
- android::ResTable::resource_name entryName;
- if (!table.getResourceName(entry[i].map.name.ident, false, &entryName)) {
- table.unlockBag(entry);
- return nullptr;
+ const android::ResTable_map& mapEntry = entry[i].map;
+ if (Res_INTERNALID(mapEntry.name.ident)) {
+ switch (mapEntry.name.ident) {
+ case android::ResTable_map::ATTR_MIN:
+ s->attribute->minInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
+ case android::ResTable_map::ATTR_MAX:
+ s->attribute->maxInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
}
-
- const ResourceType* parsedType = parseResourceType(
- StringPiece16(entryName.type, entryName.typeLen));
- if (!parsedType) {
- table.unlockBag(entry);
- return nullptr;
- }
-
- Attribute::Symbol symbol;
- symbol.symbol.name = ResourceNameRef(
- StringPiece16(entryName.package, entryName.packageLen),
- *parsedType,
- StringPiece16(entryName.name, entryName.nameLen)).toResourceName();
- symbol.symbol.id = ResourceId(entry[i].map.name.ident);
- symbol.value = entry[i].map.value.data;
- s->attribute->symbols.push_back(std::move(symbol));
+ continue;
}
+
+ android::ResTable::resource_name entryName;
+ if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ const ResourceType* parsedType = parseResourceType(
+ StringPiece16(entryName.type, entryName.typeLen));
+ if (!parsedType) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ Attribute::Symbol symbol;
+ symbol.symbol.name = ResourceName(
+ StringPiece16(entryName.package, entryName.packageLen),
+ *parsedType,
+ StringPiece16(entryName.name, entryName.nameLen));
+ symbol.symbol.id = ResourceId(mapEntry.name.ident);
+ symbol.value = mapEntry.value.data;
+ s->attribute->symbols.push_back(std::move(symbol));
}
}
table.unlockBag(entry);
@@ -148,15 +151,25 @@
for (const auto& asset : mAssets) {
const android::ResTable& table = asset->getResources(false);
StringPiece16 typeStr = toString(name.type);
+ uint32_t typeSpecFlags = 0;
ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
typeStr.data(), typeStr.size(),
- name.package.data(), name.package.size());
+ name.package.data(), name.package.size(),
+ &typeSpecFlags);
if (!resId.isValid()) {
continue;
}
- std::shared_ptr<Symbol> s = lookupIdInTable(table, resId);
+ std::shared_ptr<Symbol> s;
+ if (name.type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, resId);
+ } else {
+ s = std::make_shared<Symbol>();
+ s->id = resId;
+ }
+
if (s) {
+ s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
mCache.put(name, s);
return s.get();
}
@@ -164,6 +177,44 @@
return nullptr;
}
+static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
+ android::ResTable::resource_name resName;
+ if (!table.getResourceName(id.id, true, &resName)) {
+ return {};
+ }
+
+ ResourceName name;
+ if (resName.package) {
+ name.package = StringPiece16(resName.package, resName.packageLen).toString();
+ }
+
+ const ResourceType* type;
+ if (resName.type) {
+ type = parseResourceType(StringPiece16(resName.type, resName.typeLen));
+
+ } else if (resName.type8) {
+ type = parseResourceType(util::utf8ToUtf16(StringPiece(resName.type8, resName.typeLen)));
+ } else {
+ return {};
+ }
+
+ if (!type) {
+ return {};
+ }
+
+ name.type = *type;
+
+ if (resName.name) {
+ name.entry = StringPiece16(resName.name, resName.nameLen).toString();
+ } else if (resName.name8) {
+ name.entry = util::utf8ToUtf16(StringPiece(resName.name8, resName.nameLen));
+ } else {
+ return {};
+ }
+
+ return name;
+}
+
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
ResourceId id) {
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
@@ -173,8 +224,24 @@
for (const auto& asset : mAssets) {
const android::ResTable& table = asset->getResources(false);
- std::shared_ptr<Symbol> s = lookupIdInTable(table, id);
+ Maybe<ResourceName> maybeName = getResourceName(table, id);
+ if (!maybeName) {
+ continue;
+ }
+
+ uint32_t typeSpecFlags = 0;
+ table.getResourceFlags(id.id, &typeSpecFlags);
+
+ std::shared_ptr<Symbol> s;
+ if (maybeName.value().type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, id);
+ } else {
+ s = std::make_shared<Symbol>();
+ s->id = id;
+ }
+
if (s) {
+ s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
mIdCache.put(id, s);
return s.get();
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 89cd972..f8e3d03 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -19,10 +19,9 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "XmlDom.h"
-#include "util/Util.h"
-
#include "test/Common.h"
+#include "util/Util.h"
+#include "xml/XmlDom.h"
#include <memory>
@@ -37,6 +36,10 @@
public:
ResourceTableBuilder() = default;
+ StringPool* getStringPool() {
+ return &mTable->stringPool;
+ }
+
ResourceTableBuilder& setPackageId(const StringPiece16& packageName, uint8_t id) {
ResourceTablePackage* package = mTable->createPackage(packageName, id);
assert(package);
@@ -212,15 +215,22 @@
}
};
-inline std::unique_ptr<XmlResource> buildXmlDom(const StringPiece& str) {
+inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) {
std::stringstream in;
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
StdErrDiagnostics diag;
- std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, {});
+ std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, {});
assert(doc);
return doc;
}
+inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
+ const StringPiece& str) {
+ std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
+ doc->file.name.package = context->getCompilationPackage().toString();
+ return doc;
+}
+
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 4fa4918..555a539 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -135,8 +135,19 @@
std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
public:
+ StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
+ id, std::move(attr));
+ symbol->isPublic = true;
+ mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolTable->mIdMap[id] = symbol.get();
+ mSymbolTable->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
- std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<Attribute> attr = {}) {
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
id, std::move(attr));
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 0d17e84..5cc7aa7 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -97,19 +97,19 @@
return !error;
}
-bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
+Maybe<Reference> BinaryResourceParser::getSymbol(const void* data) {
if (!mSymbolEntries || mSymbolEntryCount == 0) {
- return false;
+ return {};
}
if ((uintptr_t) data < (uintptr_t) mData) {
- return false;
+ return {};
}
// We only support 32 bit offsets right now.
const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData;
if (offset > std::numeric_limits<uint32_t>::max()) {
- return false;
+ return {};
}
for (size_t i = 0; i < mSymbolEntryCount; i++) {
@@ -118,24 +118,23 @@
const StringPiece16 str = util::getString(
mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index));
- StringPiece16 typeStr;
- ResourceUtils::extractResourceName(str, &outSymbol->package, &typeStr,
- &outSymbol->entry);
- const ResourceType* type = parseResourceType(typeStr);
- if (!type) {
- return false;
+ ResourceNameRef nameRef;
+ bool privateRef = false;
+ if (!ResourceUtils::parseResourceName(str, &nameRef, &privateRef)) {
+ return {};
}
- outSymbol->type = *type;
-
// Since we scan the symbol table in order, we can start looking for the
// next symbol from this point.
mSymbolEntryCount -= i + 1;
mSymbolEntries += i + 1;
- return true;
+
+ Reference ref(nameRef);
+ ref.privateReference = privateRef;
+ return Maybe<Reference>(std::move(ref));
}
}
- return false;
+ return {};
}
/**
@@ -304,6 +303,11 @@
return false;
}
+ // There can be multiple packages in a table, so
+ // clear the type and key pool in case they were set from a previous package.
+ mTypePool.uninit();
+ mKeyPool.uninit();
+
ResChunkPullParser parser(getChunkData(&packageHeader->header),
getChunkDataLen(&packageHeader->header));
while (ResChunkPullParser::isGoodEvent(parser.next())) {
@@ -561,15 +565,20 @@
resourceValue = parseValue(name, config, value, entry->flags);
}
- assert(resourceValue && "failed to interpret valid resource");
+ if (!resourceValue) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "failed to parse value for resource " << name
+ << " (" << resId << ") with configuration '"
+ << config << "'");
+ return false;
+ }
Source source = mSource;
if (sourceBlock) {
- size_t len;
- const char* str = mSourcePool.string8At(util::deviceToHost32(sourceBlock->path.index),
- &len);
- if (str) {
- source.path.assign(str, len);
+ StringPiece path = util::getString8(mSourcePool,
+ util::deviceToHost32(sourceBlock->path.index));
+ if (!path.empty()) {
+ source.path = path.toString();
}
source.line = util::deviceToHost32(sourceBlock->line);
}
@@ -652,7 +661,7 @@
if (value->dataType == Res_value::TYPE_REFERENCE ||
value->dataType == Res_value::TYPE_ATTRIBUTE) {
const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
- Reference::Type::kResource : Reference::Type::kAttribute;
+ Reference::Type::kResource : Reference::Type::kAttribute;
if (data != 0) {
// This is a normal reference.
@@ -660,9 +669,9 @@
}
// This reference has an invalid ID. Check if it is an unresolved symbol.
- ResourceNameRef symbol;
- if (getSymbol(&value->data, &symbol)) {
- return util::make_unique<Reference>(symbol, type);
+ if (Maybe<Reference> ref = getSymbol(&value->data)) {
+ ref.value().referenceType = type;
+ return util::make_unique<Reference>(std::move(ref.value()));
}
// This is not an unresolved symbol, so it must be the magic @null reference.
@@ -710,26 +719,38 @@
if (util::deviceToHost32(map->parent.ident) == 0) {
// The parent is either not set or it is an unresolved symbol.
// Check to see if it is a symbol.
- ResourceNameRef symbol;
- if (getSymbol(&map->parent.ident, &symbol)) {
- style->parent = Reference(symbol.toResourceName());
- }
+ style->parent = getSymbol(&map->parent.ident);
+
} else {
// The parent is a regular reference to a resource.
style->parent = Reference(util::deviceToHost32(map->parent.ident));
}
for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
+ if (style->entries.empty()) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "out-of-sequence meta data in style");
+ return {};
+ }
+ collectMetaData(mapEntry, &style->entries.back().key);
+ continue;
+ }
+
style->entries.emplace_back();
Style::Entry& styleEntry = style->entries.back();
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
// The map entry's key (attribute) is not set. This must be
// a symbol reference, so resolve it.
- ResourceNameRef symbol;
- bool result = getSymbol(&mapEntry.name.ident, &symbol);
- assert(result);
- styleEntry.key.name = symbol.toResourceName();
+ Maybe<Reference> symbol = getSymbol(&mapEntry.name.ident);
+ if (!symbol) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "unresolved style attribute");
+ return {};
+ }
+ styleEntry.key = std::move(symbol.value());
+
} else {
// The map entry's key (attribute) is a regular reference.
styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
@@ -737,7 +758,9 @@
// Parse the attribute's value.
styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
- assert(styleEntry.value);
+ if (!styleEntry.value) {
+ return {};
+ }
}
return style;
}
@@ -757,21 +780,33 @@
attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
}
- if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- continue;
+ for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case ResTable_map::ATTR_MIN:
+ attr->minInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
+ case ResTable_map::ATTR_MAX:
+ attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
}
+ continue;
+ }
+ if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Attribute::Symbol symbol;
symbol.value = util::deviceToHost32(mapEntry.value.data);
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
// The map entry's key (id) is not set. This must be
// a symbol reference, so resolve it.
- ResourceNameRef symbolName;
- bool result = getSymbol(&mapEntry.name.ident, &symbolName);
- assert(result);
- symbol.symbol.name = symbolName.toResourceName();
+ Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
+ if (!ref) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "unresolved attribute symbol");
+ return {};
+ }
+ symbol.symbol = std::move(ref.value());
+
} else {
// The map entry's key (id) is a regular reference.
symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
@@ -781,15 +816,57 @@
}
}
- // TODO(adamlesinski): Find min, max, i80n, etc attributes.
+ // TODO(adamlesinski): Find i80n, attributes.
return attr;
}
+static bool isMetaDataEntry(const ResTable_map& mapEntry) {
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
+ case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
+ case ExtendedResTableMapTypes::ATTR_COMMENT:
+ return true;
+ }
+ return false;
+}
+
+bool BinaryResourceParser::collectMetaData(const ResTable_map& mapEntry, Value* value) {
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
+ value->setSource(Source(util::getString8(mSourcePool,
+ util::deviceToHost32(mapEntry.value.data))));
+ return true;
+ break;
+
+ case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
+ value->setSource(value->getSource().withLine(util::deviceToHost32(mapEntry.value.data)));
+ return true;
+ break;
+
+ case ExtendedResTableMapTypes::ATTR_COMMENT:
+ value->setComment(util::getString(mSourcePool, util::deviceToHost32(mapEntry.value.data)));
+ return true;
+ break;
+ }
+ return false;
+}
+
std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
std::unique_ptr<Array> array = util::make_unique<Array>();
+ Source source;
for (const ResTable_map& mapEntry : map) {
+ if (isMetaDataEntry(mapEntry)) {
+ if (array->items.empty()) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "out-of-sequence meta data in array");
+ return {};
+ }
+ collectMetaData(mapEntry, array->items.back().get());
+ continue;
+ }
+
array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
}
return array;
@@ -800,13 +877,27 @@
const ResTable_map_entry* map) {
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
for (const ResTable_map& mapEntry : map) {
+ if (isMetaDataEntry(mapEntry)) {
+ if (styleable->entries.empty()) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "out-of-sequence meta data in styleable");
+ return {};
+ }
+ collectMetaData(mapEntry, &styleable->entries.back());
+ continue;
+ }
+
if (util::deviceToHost32(mapEntry.name.ident) == 0) {
// The map entry's key (attribute) is not set. This must be
// a symbol reference, so resolve it.
- ResourceNameRef symbol;
- bool result = getSymbol(&mapEntry.name.ident, &symbol);
- assert(result);
- styleable->entries.emplace_back(symbol);
+ Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
+ if (!ref) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "unresolved styleable symbol");
+ return {};
+ }
+ styleable->entries.emplace_back(std::move(ref.value()));
+
} else {
// The map entry's key (attribute) is a regular reference.
styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident));
@@ -819,26 +910,42 @@
const ConfigDescription& config,
const ResTable_map_entry* map) {
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ Item* lastEntry = nullptr;
for (const ResTable_map& mapEntry : map) {
+ if (isMetaDataEntry(mapEntry)) {
+ if (!lastEntry) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "out-of-sequence meta data in plural");
+ return {};
+ }
+ collectMetaData(mapEntry, lastEntry);
+ continue;
+ }
+
std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
+ if (!item) {
+ return {};
+ }
+
+ lastEntry = item.get();
switch (util::deviceToHost32(mapEntry.name.ident)) {
- case android::ResTable_map::ATTR_ZERO:
+ case ResTable_map::ATTR_ZERO:
plural->values[Plural::Zero] = std::move(item);
break;
- case android::ResTable_map::ATTR_ONE:
+ case ResTable_map::ATTR_ONE:
plural->values[Plural::One] = std::move(item);
break;
- case android::ResTable_map::ATTR_TWO:
+ case ResTable_map::ATTR_TWO:
plural->values[Plural::Two] = std::move(item);
break;
- case android::ResTable_map::ATTR_FEW:
+ case ResTable_map::ATTR_FEW:
plural->values[Plural::Few] = std::move(item);
break;
- case android::ResTable_map::ATTR_MANY:
+ case ResTable_map::ATTR_MANY:
plural->values[Plural::Many] = std::move(item);
break;
- case android::ResTable_map::ATTR_OTHER:
+ case ResTable_map::ATTR_OTHER:
plural->values[Plural::Other] = std::move(item);
break;
}
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 02c4081..0745a59 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -57,7 +57,7 @@
private:
// Helper method to retrieve the symbol name for a given table offset specified
// as a pointer.
- bool getSymbol(const void* data, ResourceNameRef* outSymbol);
+ Maybe<Reference> getSymbol(const void* data);
bool parseTable(const android::ResChunk_header* chunk);
bool parseSymbolTable(const android::ResChunk_header* chunk);
@@ -91,6 +91,13 @@
const ConfigDescription& config,
const android::ResTable_map_entry* map);
+ /**
+ * If the mapEntry is a special type that denotes meta data (source, comment), then it is
+ * read and added to the Value.
+ * Returns true if the mapEntry was meta data.
+ */
+ bool collectMetaData(const android::ResTable_map& mapEntry, Value* value);
+
IAaptContext* mContext;
ResourceTable* mTable;
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 1f7d5ce..aa409ea 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -275,6 +275,29 @@
return Maybe<T>();
}
+/**
+ * Define the == operator between Maybe<T> and Maybe<U> if the operator T == U is defined.
+ * Otherwise this won't be defined and the compiler will yell at the callsite instead of inside
+ * Maybe.h.
+ */
+template <typename T, typename U>
+auto operator==(const Maybe<T>& a, const Maybe<U>& b)
+-> decltype(std::declval<T> == std::declval<U>) {
+ if (a && b) {
+ return a.value() == b.value();
+ }
+ return false;
+}
+
+/**
+ * Same as operator== but negated.
+ */
+template <typename T, typename U>
+auto operator!=(const Maybe<T>& a, const Maybe<U>& b)
+-> decltype(std::declval<T> == std::declval<U>) {
+ return !(a == b);
+}
+
} // namespace aapt
#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index d2c33ca..9cca40e 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -119,4 +119,14 @@
}
}
+TEST(MaybeTest, Equality) {
+ Maybe<int> a = 1;
+ Maybe<int> b = 1;
+ Maybe<int> c;
+
+ EXPECT_EQ(a, b);
+ EXPECT_EQ(b, a);
+ EXPECT_NE(a, c);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 59b8385..9ecc974 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -28,9 +28,6 @@
namespace aapt {
namespace util {
-constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
-constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/";
-
static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
const std::function<char(char)>& f) {
std::vector<std::string> parts;
@@ -467,18 +464,6 @@
return data;
}
-Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri) {
- if (stringStartsWith<char16_t>(namespaceUri, kSchemaPrefix)) {
- StringPiece16 schemaPrefix = kSchemaPrefix;
- StringPiece16 package = namespaceUri;
- return package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size())
- .toString();
- } else if (namespaceUri == kSchemaAuto) {
- return std::u16string();
- }
- return {};
-}
-
bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix,
StringPiece16* outEntry, StringPiece16* outSuffix) {
if (!stringStartsWith<char16_t>(path, u"res/")) {
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 80552a5..0dacbd7 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -158,6 +158,15 @@
return StringPiece16();
}
+inline StringPiece getString8(const android::ResStringPool& pool, size_t idx) {
+ size_t len;
+ const char* str = pool.string8At(idx, &len);
+ if (str != nullptr) {
+ return StringPiece(str, len);
+ }
+ return StringPiece();
+}
+
/**
* Checks that the Java string format contains no non-positional arguments (arguments without
* explicitly specifying an index) when there are more than one argument. This is an error
@@ -229,11 +238,12 @@
private:
friend class Tokenizer<Char>;
- iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok);
+ iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok, bool end);
- BasicStringPiece<Char> str;
- Char separator;
- BasicStringPiece<Char> token;
+ BasicStringPiece<Char> mStr;
+ Char mSeparator;
+ BasicStringPiece<Char> mToken;
+ bool mEnd;
};
Tokenizer(BasicStringPiece<Char> str, Char sep);
@@ -252,36 +262,38 @@
template <typename Char>
typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() {
- const Char* start = token.end();
- const Char* end = str.end();
+ const Char* start = mToken.end();
+ const Char* end = mStr.end();
if (start == end) {
- token.assign(token.end(), 0);
+ mEnd = true;
+ mToken.assign(mToken.end(), 0);
return *this;
}
start += 1;
const Char* current = start;
while (current != end) {
- if (*current == separator) {
- token.assign(start, current - start);
+ if (*current == mSeparator) {
+ mToken.assign(start, current - start);
return *this;
}
++current;
}
- token.assign(start, end - start);
+ mToken.assign(start, end - start);
return *this;
}
template <typename Char>
inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() {
- return token;
+ return mToken;
}
template <typename Char>
inline bool Tokenizer<Char>::iterator::operator==(const iterator& rhs) const {
// We check equality here a bit differently.
// We need to know that the addresses are the same.
- return token.begin() == rhs.token.begin() && token.end() == rhs.token.end();
+ return mToken.begin() == rhs.mToken.begin() && mToken.end() == rhs.mToken.end() &&
+ mEnd == rhs.mEnd;
}
template <typename Char>
@@ -291,8 +303,8 @@
template <typename Char>
inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep,
- BasicStringPiece<Char> tok) :
- str(s), separator(sep), token(tok) {
+ BasicStringPiece<Char> tok, bool end) :
+ mStr(s), mSeparator(sep), mToken(tok), mEnd(end) {
}
template <typename Char>
@@ -307,8 +319,8 @@
template <typename Char>
inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) :
- mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0))),
- mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) {
+ mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0), false)),
+ mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0), true) {
}
inline uint16_t hostToDevice16(uint16_t value) {
@@ -328,15 +340,6 @@
}
/**
- * Returns a package name if the namespace URI is of the form:
- * http://schemas.android.com/apk/res/<package>
- *
- * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
- * returns an empty package name.
- */
-Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri);
-
-/**
* Given a path like: res/xml-sw600dp/foo.xml
*
* Extracts "res/xml-sw600dp/" into outPrefix.
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 9db9fb7..9208e07 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -101,6 +101,15 @@
ASSERT_EQ(tokenizer.end(), iter);
}
+TEST(UtilTest, TokenizeEmptyString) {
+ auto tokenizer = util::tokenize(StringPiece16(u""), u'|');
+ auto iter = tokenizer.begin();
+ ASSERT_NE(tokenizer.end(), iter);
+ ASSERT_EQ(StringPiece16(), *iter);
+ ++iter;
+ ASSERT_EQ(tokenizer.end(), iter);
+}
+
TEST(UtilTest, TokenizeAtEnd) {
auto tokenizer = util::tokenize(StringPiece16(u"one."), u'.');
auto iter = tokenizer.begin();
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
similarity index 91%
rename from tools/aapt2/XmlDom.cpp
rename to tools/aapt2/xml/XmlDom.cpp
index b769c76..d27b62fd 100644
--- a/tools/aapt2/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "util/Util.h"
#include "XmlDom.h"
#include "XmlPullParser.h"
+#include "util/Util.h"
#include <cassert>
+#include <expat.h>
#include <memory>
#include <stack>
#include <string>
@@ -317,6 +318,10 @@
return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
}
+Element* findRootElement(XmlResource* doc) {
+ return findRootElement(doc->root.get());
+}
+
Element* findRootElement(Node* node) {
if (!node) {
return nullptr;
@@ -397,5 +402,39 @@
return elements;
}
+void PackageAwareVisitor::visit(Namespace* ns) {
+ bool added = false;
+ if (Maybe<ExtractedPackage> maybePackage = extractPackageFromNamespace(ns->namespaceUri)) {
+ ExtractedPackage& package = maybePackage.value();
+ mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, std::move(package) });
+ added = true;
+ }
+
+ Visitor::visit(ns);
+
+ if (added) {
+ mPackageDecls.pop_back();
+ }
+}
+
+Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const {
+ if (alias.empty()) {
+ return ExtractedPackage{ localPackage.toString(), false /* private */ };
+ }
+
+ const auto rend = mPackageDecls.rend();
+ for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
+ if (alias == iter->prefix) {
+ if (iter->package.package.empty()) {
+ return ExtractedPackage{ localPackage.toString(),
+ iter->package.privateNamespace };
+ }
+ return iter->package;
+ }
+ }
+ return {};
+}
+
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/xml/XmlDom.h
similarity index 76%
rename from tools/aapt2/XmlDom.h
rename to tools/aapt2/xml/XmlDom.h
index 9a46bcb..033b0a4 100644
--- a/tools/aapt2/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -22,11 +22,9 @@
#include "ResourceValues.h"
#include "util/StringPiece.h"
#include "util/Util.h"
-
-#include "process/IResourceTableConsumer.h"
+#include "xml/XmlUtil.h"
#include <istream>
-#include <expat.h>
#include <memory>
#include <string>
#include <vector>
@@ -34,21 +32,9 @@
namespace aapt {
namespace xml {
-constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
-
struct RawVisitor;
/**
- * The type of node. Can be used to downcast to the concrete XML node
- * class.
- */
-enum class NodeType {
- kNamespace,
- kElement,
- kText,
-};
-
-/**
* Base class for all XML nodes.
*/
struct Node {
@@ -58,9 +44,10 @@
std::u16string comment;
std::vector<std::unique_ptr<Node>> children;
+ virtual ~Node() = default;
+
void addChild(std::unique_ptr<Node> child);
virtual void accept(RawVisitor* visitor) = 0;
- virtual ~Node() {}
};
/**
@@ -122,6 +109,14 @@
};
/**
+ * An XML resource with a source, name, and XML tree.
+ */
+struct XmlResource {
+ ResourceFile file;
+ std::unique_ptr<xml::Node> root;
+};
+
+/**
* Inflates an XML DOM from a text stream, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
@@ -134,6 +129,7 @@
std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
const Source& source);
+Element* findRootElement(XmlResource* doc);
Element* findRootElement(Node* node);
/**
@@ -180,7 +176,7 @@
private:
struct PackageDecl {
std::u16string prefix;
- std::u16string package;
+ ExtractedPackage package;
};
std::vector<PackageDecl> mPackageDecls;
@@ -188,41 +184,9 @@
public:
using Visitor::visit;
- void visit(Namespace* ns) override {
- bool added = false;
- {
- Maybe<std::u16string> package = util::extractPackageFromNamespace(ns->namespaceUri);
- if (package) {
- mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, package.value() });
- added = true;
- }
- }
-
- Visitor::visit(ns);
-
- if (added) {
- mPackageDecls.pop_back();
- }
- }
-
- Maybe<ResourceName> transformPackage(const ResourceName& name,
- const StringPiece16& localPackage) const override {
- if (name.package.empty()) {
- return ResourceName{ localPackage.toString(), name.type, name.entry };
- }
-
- const auto rend = mPackageDecls.rend();
- for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
- if (name.package == iter->prefix) {
- if (iter->package.empty()) {
- return ResourceName{ localPackage.toString(), name.type, name.entry };
- } else {
- return ResourceName{ iter->package, name.type, name.entry };
- }
- }
- }
- return {};
- }
+ void visit(Namespace* ns) override;
+ Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const override;
};
// Implementations
diff --git a/tools/aapt2/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
similarity index 93%
rename from tools/aapt2/XmlDom_test.cpp
rename to tools/aapt2/xml/XmlDom_test.cpp
index a1b9ed0..431ee2c 100644
--- a/tools/aapt2/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "XmlDom.h"
+#include "xml/XmlDom.h"
#include <gtest/gtest.h>
#include <sstream>
@@ -38,7 +38,7 @@
const Source source = { "test.xml" };
StdErrDiagnostics diag;
- std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, source);
+ std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, source);
ASSERT_NE(doc, nullptr);
xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get());
diff --git a/tools/aapt2/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
similarity index 85%
rename from tools/aapt2/XmlPullParser.cpp
rename to tools/aapt2/xml/XmlPullParser.cpp
index cff935c..323ec05 100644
--- a/tools/aapt2/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -16,12 +16,14 @@
#include "util/Maybe.h"
#include "util/Util.h"
-#include "XmlPullParser.h"
+#include "xml/XmlPullParser.h"
+#include "xml/XmlUtil.h"
#include <iostream>
#include <string>
namespace aapt {
+namespace xml {
constexpr char kXmlNamespaceSep = 1;
@@ -72,14 +74,14 @@
// Record namespace prefixes and package names so that we can do our own
// handling of references that use namespace aliases.
if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
- Maybe<std::u16string> result = util::extractPackageFromNamespace(getNamespaceUri());
+ Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri());
if (event == Event::kStartNamespace) {
if (result) {
- mPackageAliases.emplace_back(getNamespacePrefix(), result.value());
+ mPackageAliases.emplace_back(
+ PackageDecl{ getNamespacePrefix(), std::move(result.value()) });
}
} else {
if (result) {
- assert(mPackageAliases.back().second == result.value());
mPackageAliases.pop_back();
}
}
@@ -131,20 +133,20 @@
return mEventQueue.front().data2;
}
-Maybe<ResourceName> XmlPullParser::transformPackage(
- const ResourceName& name, const StringPiece16& localPackage) const {
- if (name.package.empty()) {
- return ResourceName{ localPackage.toString(), name.type, name.entry };
+Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const {
+ if (alias.empty()) {
+ return ExtractedPackage{ localPackage.toString(), false /* private */ };
}
const auto endIter = mPackageAliases.rend();
for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
- if (name.package == iter->first) {
- if (iter->second.empty()) {
- return ResourceName{ localPackage.toString(), name.type, name.entry };
- } else {
- return ResourceName{ iter->second, name.type, name.entry };
+ if (alias == iter->prefix) {
+ if (iter->package.package.empty()) {
+ return ExtractedPackage{ localPackage.toString(),
+ iter->package.privateNamespace };
}
+ return iter->package;
}
}
return {};
@@ -283,4 +285,24 @@
});
}
+Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) {
+ auto iter = parser->findAttribute(u"", name);
+ if (iter != parser->endAttributes()) {
+ return StringPiece16(util::trimWhitespace(iter->value));
+ }
+ return {};
+}
+
+Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) {
+ auto iter = parser->findAttribute(u"", name);
+ if (iter != parser->endAttributes()) {
+ StringPiece16 trimmed = util::trimWhitespace(iter->value);
+ if (!trimmed.empty()) {
+ return trimmed;
+ }
+ }
+ return {};
+}
+
+} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
similarity index 91%
rename from tools/aapt2/XmlPullParser.h
rename to tools/aapt2/xml/XmlPullParser.h
index a0ce21d..7e7070e 100644
--- a/tools/aapt2/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -17,11 +17,11 @@
#ifndef AAPT_XML_PULL_PARSER_H
#define AAPT_XML_PULL_PARSER_H
-#include "util/Maybe.h"
#include "Resource.h"
-#include "util/StringPiece.h"
-
#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "xml/XmlUtil.h"
#include <algorithm>
#include <expat.h>
@@ -33,6 +33,7 @@
#include <vector>
namespace aapt {
+namespace xml {
class XmlPullParser : public IPackageDeclStack {
public:
@@ -60,7 +61,7 @@
static bool isGoodEvent(Event event);
XmlPullParser(std::istream& in);
- virtual ~XmlPullParser();
+ ~XmlPullParser();
/**
* Returns the current event that is being processed.
@@ -95,6 +96,13 @@
const std::u16string& getNamespacePrefix() const;
const std::u16string& getNamespaceUri() const;
+ //
+ // These are available for StartElement and EndElement.
+ //
+
+ const std::u16string& getElementNamespace() const;
+ const std::u16string& getElementName() const;
+
/*
* Uses the current stack of namespaces to resolve the package. Eg:
* xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
@@ -106,17 +114,8 @@
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- //
-
- //
- // These are available for StartElement and EndElement.
- //
-
- const std::u16string& getElementNamespace() const;
- const std::u16string& getElementName() const;
-
- Maybe<ResourceName> transformPackage(const ResourceName& name,
- const StringPiece16& localPackage) const override;
+ Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const override;
//
// Remaining methods are for retrieving information about attributes
@@ -169,9 +168,25 @@
const std::u16string mEmpty;
size_t mDepth;
std::stack<std::u16string> mNamespaceUris;
- std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
+
+ struct PackageDecl {
+ std::u16string prefix;
+ ExtractedPackage package;
+ };
+ std::vector<PackageDecl> mPackageAliases;
};
+/**
+ * Finds the attribute in the current element within the global namespace.
+ */
+Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name);
+
+/**
+ * Finds the attribute in the current element within the global namespace. The attribute's value
+ * must not be the empty string.
+ */
+Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name);
+
//
// Implementation
//
@@ -277,6 +292,7 @@
return endIter;
}
+} // namespace xml
} // namespace aapt
#endif // AAPT_XML_PULL_PARSER_H
diff --git a/tools/aapt2/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
similarity index 63%
rename from tools/aapt2/XmlPullParser_test.cpp
rename to tools/aapt2/xml/XmlPullParser_test.cpp
index 1c99a43..8fa2c6d 100644
--- a/tools/aapt2/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -15,7 +15,7 @@
*/
#include "util/StringPiece.h"
-#include "XmlPullParser.h"
+#include "xml/XmlPullParser.h"
#include <gtest/gtest.h>
#include <sstream>
@@ -26,30 +26,30 @@
std::stringstream str;
str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
- XmlPullParser parser(str);
+ xml::XmlPullParser parser(str);
const size_t depthOuter = parser.getDepth();
- ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthOuter));
+ ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName()));
const size_t depthA = parser.getDepth();
- ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthA));
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName()));
const size_t depthB = parser.getDepth();
- ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName()));
- ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB));
- EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent());
+ ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
+ EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName()));
- ASSERT_FALSE(XmlPullParser::nextChildNode(&parser, depthOuter));
- EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.getEvent());
+ ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
+ EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent());
}
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
new file mode 100644
index 0000000..ab9f544
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util/Maybe.h"
+#include "util/Util.h"
+#include "xml/XmlUtil.h"
+
+#include <string>
+
+namespace aapt {
+namespace xml {
+
+Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri) {
+ if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPublicPrefix)) {
+ StringPiece16 schemaPrefix = kSchemaPublicPrefix;
+ StringPiece16 package = namespaceUri;
+ package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
+ if (package.empty()) {
+ return {};
+ }
+ return ExtractedPackage{ package.toString(), false /* isPrivate */ };
+
+ } else if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPrivatePrefix)) {
+ StringPiece16 schemaPrefix = kSchemaPrivatePrefix;
+ StringPiece16 package = namespaceUri;
+ package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
+ if (package.empty()) {
+ return {};
+ }
+ return ExtractedPackage{ package.toString(), true /* isPrivate */ };
+
+ } else if (namespaceUri == kSchemaAuto) {
+ return ExtractedPackage{ std::u16string(), true /* isPrivate */ };
+ }
+ return {};
+}
+
+void transformReferenceFromNamespace(IPackageDeclStack* declStack,
+ const StringPiece16& localPackage, Reference* inRef) {
+ if (inRef->name) {
+ if (Maybe<ExtractedPackage> transformedPackage =
+ declStack->transformPackageAlias(inRef->name.value().package, localPackage)) {
+ ExtractedPackage& extractedPackage = transformedPackage.value();
+ inRef->name.value().package = std::move(extractedPackage.package);
+
+ // If the reference was already private (with a * prefix) and the namespace is public,
+ // we keep the reference private.
+ inRef->privateReference |= extractedPackage.privateNamespace;
+ }
+ }
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
new file mode 100644
index 0000000..98e5520
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XML_XMLUTIL_H
+#define AAPT_XML_XMLUTIL_H
+
+#include "ResourceValues.h"
+#include "util/Maybe.h"
+
+#include <string>
+
+namespace aapt {
+namespace xml {
+
+constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
+constexpr const char16_t* kSchemaPublicPrefix = u"http://schemas.android.com/apk/res/";
+constexpr const char16_t* kSchemaPrivatePrefix = u"http://schemas.android.com/apk/prv/res/";
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+/**
+ * Result of extracting a package name from a namespace URI declaration.
+ */
+struct ExtractedPackage {
+ /**
+ * The name of the package. This can be the empty string, which means that the package
+ * should be assumed to be the package being compiled.
+ */
+ std::u16string package;
+
+ /**
+ * True if the package's private namespace was declared. This means that private resources
+ * are made visible.
+ */
+ bool privateNamespace;
+};
+
+/**
+ * Returns an ExtractedPackage struct if the namespace URI is of the form:
+ * http://schemas.android.com/apk/res/<package> or
+ * http://schemas.android.com/apk/prv/res/<package>
+ *
+ * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
+ * returns an empty package name.
+ */
+Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri);
+
+/**
+ * Interface representing a stack of XML namespace declarations. When looking up the package
+ * for a namespace prefix, the stack is checked from top to bottom.
+ */
+struct IPackageDeclStack {
+ virtual ~IPackageDeclStack() = default;
+
+ /**
+ * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
+ */
+ virtual Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece16& alias, const StringPiece16& localPackage) const = 0;
+};
+
+/**
+ * Helper function for transforming the original Reference inRef to a fully qualified reference
+ * via the IPackageDeclStack. This will also mark the Reference as private if the namespace of
+ * the package declaration was private.
+ */
+void transformReferenceFromNamespace(IPackageDeclStack* declStack,
+ const StringPiece16& localPackage, Reference* inRef);
+
+} // namespace xml
+} // namespace aapt
+
+#endif /* AAPT_XML_XMLUTIL_H */
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
new file mode 100644
index 0000000..7796b3e
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Common.h"
+#include "xml/XmlUtil.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(XmlUtilTest, ExtractPackageFromNamespace) {
+ AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"com.android"));
+ AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk"));
+ AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res"));
+ AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/"));
+ AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(
+ u"http://schemas.android.com/apk/prv/res/"));
+
+ Maybe<xml::ExtractedPackage> p =
+ xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::u16string(u"a"), p.value().package);
+ EXPECT_EQ(false, p.value().privateNamespace);
+
+ p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::u16string(u"android"), p.value().package);
+ EXPECT_EQ(true, p.value().privateNamespace);
+
+ p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::u16string(u"com.test"), p.value().package);
+ EXPECT_EQ(true, p.value().privateNamespace);
+
+ p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto");
+ AAPT_ASSERT_TRUE(p);
+ EXPECT_EQ(std::u16string(), p.value().package);
+ EXPECT_EQ(true, p.value().privateNamespace);
+}
+
+} // namespace aapt
diff --git a/tools/layoutlib/.idea/encodings.xml b/tools/layoutlib/.idea/encodings.xml
index e206d70..f758959 100644
--- a/tools/layoutlib/.idea/encodings.xml
+++ b/tools/layoutlib/.idea/encodings.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
- <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
-</project>
-
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 61ddb04..53bfc15 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -30,6 +30,9 @@
built_framework_dep := $(call java-lib-deps,framework)
built_framework_classes := $(call java-lib-files,framework)
+built_oj_dep := $(call java-lib-deps,core-oj)
+built_oj_classes := $(call java-lib-files,core-oj)
+
built_core_dep := $(call java-lib-deps,core-libart)
built_core_classes := $(call java-lib-files,core-libart)
@@ -56,7 +59,8 @@
include $(BUILD_SYSTEM)/base_rules.mk
#######################################
-$(LOCAL_BUILT_MODULE): $(built_core_dep) \
+$(LOCAL_BUILT_MODULE): $(built_oj_dep) \
+ $(built_core_dep) \
$(built_framework_dep) \
$(built_ext_dep) \
$(built_ext_data) \
@@ -69,6 +73,7 @@
$(hide) ls -l $(built_framework_classes)
$(hide) java -ea -jar $(built_layoutlib_create_jar) \
$@ \
+ $(built_oj_classes) \
$(built_core_classes) \
$(built_framework_classes) \
$(built_ext_classes) \
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 5db1bde..723e827 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -23,6 +23,7 @@
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.MockView;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
@@ -126,6 +127,9 @@
if (view == null) {
view = loadCustomView(name, attrs);
}
+ } catch (InflateException e) {
+ // Don't catch the InflateException below as that results in hiding the real cause.
+ throw e;
} catch (Exception e) {
// Wrap the real exception in a ClassNotFoundException, so that the calling method
// can deal with it.
@@ -154,23 +158,30 @@
}
ta.recycle();
}
- final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = context;
- // try to load the class from using the custom view loader
- try {
- view = loadCustomView(name, attrs);
- } catch (Exception e2) {
- // Wrap the real exception in an InflateException so that the calling
- // method can deal with it.
- InflateException exception = new InflateException();
- if (!e2.getClass().equals(ClassNotFoundException.class)) {
- exception.initCause(e2);
- } else {
- exception.initCause(e);
+ if (!(e.getCause() instanceof ClassNotFoundException)) {
+ // There is some unknown inflation exception in inflating a View that was found.
+ view = new MockView(context, attrs);
+ ((MockView) view).setText(name);
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null);
+ } else {
+ final Object lastContext = mConstructorArgs[0];
+ mConstructorArgs[0] = context;
+ // try to load the class from using the custom view loader
+ try {
+ view = loadCustomView(name, attrs);
+ } catch (Exception e2) {
+ // Wrap the real exception in an InflateException so that the calling
+ // method can deal with it.
+ InflateException exception = new InflateException();
+ if (!e2.getClass().equals(ClassNotFoundException.class)) {
+ exception.initCause(e2);
+ } else {
+ exception.initCause(e);
+ }
+ throw exception;
+ } finally {
+ mConstructorArgs[0] = lastContext;
}
- throw exception;
- } finally {
- mConstructorArgs[0] = lastContext;
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 3c260a8..6951ede 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -298,7 +298,7 @@
@Override
public Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth,
- int maxHeight) throws RemoteException {
+ int maxHeight, float frameScale) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 44a9aad..d392f21 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -17,39 +17,90 @@
package com.android.layoutlib.bridge;
import android.content.Context;
-import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.TextView;
/**
* Base class for mocked views.
- *
- * TODO: implement onDraw and draw a rectangle in a random color with the name of the class
- * (or better the id of the view).
+ * <p/>
+ * FrameLayout with a single TextView. Doesn't allow adding any other views to itself.
*/
-public class MockView extends TextView {
+public class MockView extends FrameLayout {
+
+ private final TextView mView;
+
+ public MockView(Context context) {
+ this(context, null);
+ }
public MockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public MockView(Context context, AttributeSet attrs, int defStyle) {
- this(context, attrs, defStyle, 0);
+ public MockView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
-
- setText(this.getClass().getSimpleName());
- setTextColor(0xFF000000);
+ mView = new TextView(context, attrs);
+ mView.setTextColor(0xFF000000);
setGravity(Gravity.CENTER);
+ setText(getClass().getSimpleName());
+ addView(mView);
+ setBackgroundColor(0xFF7F7F7F);
+ }
+
+ // Only allow adding one TextView.
+ @Override
+ public void addView(View child) {
+ if (child == mView) {
+ super.addView(child);
+ }
}
@Override
- public void onDraw(Canvas canvas) {
- canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F);
+ public void addView(View child, int index) {
+ if (child == mView) {
+ super.addView(child, index);
+ }
+ }
- super.onDraw(canvas);
+ @Override
+ public void addView(View child, int width, int height) {
+ if (child == mView) {
+ super.addView(child, width, height);
+ }
+ }
+
+ @Override
+ public void addView(View child, ViewGroup.LayoutParams params) {
+ if (child == mView) {
+ super.addView(child, params);
+ }
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ if (child == mView) {
+ super.addView(child, index, params);
+ }
+ }
+
+ // The following methods are called by the IDE via reflection, and should be considered part
+ // of the API.
+ // Historically, MockView used to be a textView and had these methods. Now, we simply delegate
+ // them to the contained textView.
+
+ public void setText(CharSequence text) {
+ mView.setText(text);
+ }
+
+ public void setGravity(int gravity) {
+ mView.setGravity(gravity);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 1ec0547..5c73fb6a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -167,6 +167,11 @@
}
@Override
+ public void cancelDragAndDrop(IBinder dragToken) throws RemoteException {
+ // pass for now
+ }
+
+ @Override
public void dragRecipientEntered(IWindow window) throws RemoteException {
// pass for now
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index 868c6d3..cdcf0ea 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -16,10 +16,13 @@
package com.android.layoutlib.bridge.bars;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
@@ -45,6 +48,8 @@
private Object mWindowDecorActionBar;
private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
+ // This is used on v23.1.1 and later.
+ private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar";
private Class<?> mWindowActionBarClass;
/**
@@ -70,14 +75,25 @@
try {
Class[] constructorParams = {View.class};
Object[] constructorArgs = {getDecorContent()};
- mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS,
- constructorParams, constructorArgs);
+ LayoutlibCallback callback = params.getLayoutlibCallback();
+ // Check if the old action bar class is present.
+ String actionBarClass = WINDOW_ACTION_BAR_CLASS;
+ try {
+ callback.findClass(actionBarClass);
+ } catch (ClassNotFoundException expected) {
+ // Failed to find the old class, use the newer one.
+ actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW;
+ }
+
+ mWindowDecorActionBar = callback.loadView(actionBarClass,
+ constructorParams, constructorArgs);
mWindowActionBarClass = mWindowDecorActionBar == null ? null :
mWindowDecorActionBar.getClass();
setupActionBar();
} catch (Exception e) {
- e.printStackTrace();
+ Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
+ "Failed to load AppCompat ActionBar with unknown error.", e);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 42e55e2..a6e5fb8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,7 +33,6 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -228,18 +227,16 @@
* Find the background color for this bar from the theme attributes. Only relevant to StatusBar
* and NavigationBar.
* <p/>
- * Returns null if not found.
+ * Returns 0 if not found.
*
* @param colorAttrName the attribute name for the background color
* @param translucentAttrName the attribute name for the translucency property of the bar.
*
* @throws NumberFormatException if color resolved to an invalid string.
*/
- @Nullable
- protected Integer getBarColor(@NonNull String colorAttrName,
- @NonNull String translucentAttrName) {
+ protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
- return null;
+ return 0;
}
RenderResources renderResources = getContext().getRenderResources();
// First check if the bar is translucent.
@@ -254,11 +251,10 @@
if (transparent) {
return getColor(renderResources, colorAttrName);
}
- return null;
+ return 0;
}
- @Nullable
- private static Integer getColor(RenderResources renderResources, String attr) {
+ private static int getColor(RenderResources renderResources, String attr) {
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attr, true);
// Form @color/bar to the #AARRGGBB
@@ -279,7 +275,7 @@
}
}
}
- return null;
+ return 0;
}
private ResourceValue getResourceValue(String reference) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index d50ce23..9c89bfe 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -65,8 +65,8 @@
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
- Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == null ? 0xFF000000 : color);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == 0 ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 95a5a58..2dc7c65 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -71,9 +71,8 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(
- color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 2a4f583..0ffa357 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,7 +421,8 @@
gc.setComposite(AlphaComposite.Src);
gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+ gc.fillRect(0, 0,
+ mMeasuredScreenWidth, mMeasuredScreenHeight);
// done
gc.dispose();
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index ae4a57d..7ef7566 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -17,6 +17,7 @@
package com.android.tools.layoutlib.create;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -40,6 +41,7 @@
private final String mClassName;
private final Set<String> mDelegateMethods;
private final Log mLog;
+ private boolean mIsStaticInnerClass;
/**
* Creates a new {@link DelegateClassAdapter} that can transform some methods
@@ -62,16 +64,30 @@
mLog = log;
mClassName = className;
mDelegateMethods = delegateMethods;
+ // If this is an inner class, by default, we assume it's static. If it's not we will detect
+ // by looking at the fields (see visitField)
+ mIsStaticInnerClass = className.contains("$");
}
//----------------------------------
// Methods from the ClassAdapter
@Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ if (mIsStaticInnerClass && "this$0".equals(name)) {
+ // Having a "this$0" field, proves that this class is not a static inner class.
+ mIsStaticInnerClass = false;
+ }
+
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ @Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
- boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+ boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
@@ -96,7 +112,8 @@
MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
DelegateMethodAdapter a = new DelegateMethodAdapter(
- mLog, null, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
// A native has no code to visit, so we need to generate it directly.
a.generateDelegateCode();
@@ -120,6 +137,7 @@
desc, signature, exceptions);
return new DelegateMethodAdapter(
- mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index 12690db..cca9e57 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -85,6 +85,8 @@
private String mDesc;
/** True if the original method is static. */
private final boolean mIsStatic;
+ /** True if the method is contained in a static inner class */
+ private final boolean mIsStaticInnerClass;
/** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
private final String mClassName;
/** The method name. */
@@ -120,7 +122,8 @@
String className,
String methodName,
String desc,
- boolean isStatic) {
+ boolean isStatic,
+ boolean isStaticClass) {
super(Opcodes.ASM4);
mLog = log;
mOrgWriter = mvOriginal;
@@ -129,6 +132,7 @@
mMethodName = methodName;
mDesc = desc;
mIsStatic = isStatic;
+ mIsStaticInnerClass = isStaticClass;
}
/**
@@ -206,7 +210,7 @@
// by the 'this' of any outer class, if any.
if (!mIsStatic) {
- if (outerType != null) {
+ if (outerType != null && !mIsStaticInnerClass) {
// The first-level inner class has a package-protected member called 'this$0'
// that points to the outer class.
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index 648cea43..e37a09b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -27,6 +27,7 @@
import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
import com.android.tools.layoutlib.create.dataclass.OuterClass;
import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
import org.junit.Before;
import org.junit.Test;
@@ -56,6 +57,8 @@
private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
InnerClass.class.getSimpleName();
+ private static final String STATIC_INNER_CLASS_NAME =
+ OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName();
@Before
public void setUp() throws Exception {
@@ -294,6 +297,61 @@
}
}
+ @Test
+ public void testDelegateStaticInner() throws Throwable {
+ // We'll delegate the "get" method of both the inner and outer class.
+ HashSet<String> delegateMethods = new HashSet<String>();
+ delegateMethods.add("get");
+
+ // Generate the delegate for the outer class.
+ ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
+ String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvOuter = new DelegateClassAdapter(
+ mLog, cwOuter, outerClassName, delegateMethods);
+ ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+ cr.accept(cvOuter, 0 /* flags */);
+
+ // Generate the delegate for the static inner class.
+ ClassWriter cwInner = new ClassWriter(0 /*flags*/);
+ String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvInner = new DelegateClassAdapter(
+ mLog, cwInner, innerClassName, delegateMethods);
+ cr = new ClassReader(STATIC_INNER_CLASS_NAME);
+ cr.accept(cvInner, 0 /* flags */);
+
+ // Load the generated classes in a different class loader and try them
+ ClassLoader2 cl2 = null;
+ try {
+ cl2 = new ClassLoader2() {
+ @Override
+ public void testModifiedInstance() throws Exception {
+
+ // Check the outer class
+ Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
+ Object o2 = outerClazz2.newInstance();
+ assertNotNull(o2);
+
+ // Check the inner class. Since it's not a static inner class, we need
+ // to use the hidden constructor that takes the outer class as first parameter.
+ Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME);
+ Constructor<?> innerCons = innerClazz2.getConstructor();
+ Object i2 = innerCons.newInstance();
+ assertNotNull(i2);
+
+ // The original StaticInner.get returns 100+10+20,
+ // but the delegate makes it return 6+10+20
+ assertEquals(6+10+20, callGet(i2, 10, 20));
+ assertEquals(100+10+20, callGet_Original(i2, 10, 20));
+ }
+ };
+ cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
+ cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray());
+ cl2.testModifiedInstance();
+ } catch (Throwable t) {
+ throw dumpGeneratedClass(t, cl2);
+ }
+ }
+
//-------
/**
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
index f083e76..6dfb816 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -45,6 +45,16 @@
}
}
+ public static class StaticInnerClass {
+ public StaticInnerClass() {
+ }
+
+ // StaticInnerClass.get returns 100 + a + b
+ public int get(int a, long b) {
+ return 100 + a + (int) b;
+ }
+ }
+
@SuppressWarnings("unused")
private String privateMethod() {
return "outerPrivateMethod";
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
new file mode 100644
index 0000000..a29439e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * 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.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_StaticInnerClass_Delegate {
+ // The delegate override of Inner.get return 6 + a + b
+ public static int get(StaticInnerClass inner, int a, long b) {
+ return 6 + a + (int) b;
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index e611ea4..6e42391 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -737,7 +737,9 @@
public String toString() {
StringBuffer sb = new StringBuffer();
for (String key : mFields.keySet()) {
- sb.append(key).append(" ").append(mFields.get(key)).append("\n");
+ // Don't display password in toString().
+ String value = (key == PASSWORD_KEY) ? "<removed>" : mFields.get(key);
+ sb.append(key).append(" ").append(value).append("\n");
}
return sb.toString();
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index e25b38c..9f8af6e 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -424,6 +424,15 @@
return mMacAddress;
}
+ /**
+ * @return true if {@link #getMacAddress()} has a real MAC address.
+ *
+ * @hide
+ */
+ public boolean hasRealMacAddress() {
+ return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
+ }
+
/** {@hide} */
public void setMeteredHint(boolean meteredHint) {
mMeteredHint = meteredHint;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index c26ca6e..5534cad 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -168,6 +168,22 @@
* to wake up at fixed interval
*/
public int maxScansToCache;
+ /**
+ * if maxPeriodInMs is non zero or different than period, then this bucket is
+ * an exponential backoff bucket and the scan period will grow exponentially
+ * as per formula: actual_period(N) = period ^ (N/(step_count+1))
+ * to a maximum period of max_period.
+ */
+ public int maxPeriodInMs;
+ /**
+ * for exponential back off bucket: multiplier: new_period=old_period*exponent
+ */
+ public int exponent;
+ /**
+ * for exponential back off bucket, number of scans performed at a given
+ * period and until the exponent is applied
+ */
+ public int stepCount;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -181,6 +197,9 @@
dest.writeInt(reportEvents);
dest.writeInt(numBssidsPerScan);
dest.writeInt(maxScansToCache);
+ dest.writeInt(maxPeriodInMs);
+ dest.writeInt(exponent);
+ dest.writeInt(stepCount);
if (channels != null) {
dest.writeInt(channels.length);
@@ -206,6 +225,9 @@
settings.reportEvents = in.readInt();
settings.numBssidsPerScan = in.readInt();
settings.maxScansToCache = in.readInt();
+ settings.maxPeriodInMs = in.readInt();
+ settings.exponent = in.readInt();
+ settings.stepCount = in.readInt();
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {