Merge "Back key to dismiss full screen video" into jb-mr1-dev
diff --git a/Android.mk b/Android.mk
index 0665e60..cb1b90b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,6 +74,7 @@
core/java/android/app/ISearchManager.aidl \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
+ core/java/android/app/IStopUserCallback.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
@@ -115,6 +116,7 @@
core/java/android/database/IContentObserver.aidl \
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/display/IDisplayManager.aidl \
+ core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
@@ -151,6 +153,7 @@
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
+ core/java/android/view/IDisplayContentChangeListener.aidl \
core/java/android/view/IInputFilter.aidl \
core/java/android/view/IInputFilterHost.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
diff --git a/api/current.txt b/api/current.txt
index 720681a..7ad9cad 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -76,6 +76,7 @@
field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
field public static final java.lang.String NET_TUNNELING = "android.permission.NET_TUNNELING";
field public static final java.lang.String NFC = "android.permission.NFC";
+ field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR";
@@ -772,6 +773,7 @@
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
field public static final int preferenceStyle = 16842894; // 0x101008e
+ field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -945,6 +947,7 @@
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextStyle = 16843513; // 0x10102f9
field public static final int subtypeExtraValue = 16843674; // 0x101039a
+ field public static final int subtypeId = 16843713; // 0x10103c1
field public static final int subtypeLocale = 16843673; // 0x1010399
field public static final int suggestActionMsg = 16843228; // 0x10101dc
field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
@@ -2063,6 +2066,7 @@
field public static final int DEFAULT = 1; // 0x1
field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
field public static final int FEEDBACK_AUDIBLE = 4; // 0x4
+ field public static final int FEEDBACK_BRAILLE = 32; // 0x20
field public static final int FEEDBACK_GENERIC = 16; // 0x10
field public static final int FEEDBACK_HAPTIC = 2; // 0x2
field public static final int FEEDBACK_SPOKEN = 1; // 0x1
@@ -3345,6 +3349,7 @@
method public final boolean equals(java.lang.Object);
method public final android.app.Activity getActivity();
method public final android.os.Bundle getArguments();
+ method public final android.app.FragmentManager getChildFragmentManager();
method public final android.app.FragmentManager getFragmentManager();
method public final int getId();
method public android.app.LoaderManager getLoaderManager();
@@ -3396,6 +3401,7 @@
method public void onStop();
method public void onTrimMemory(int);
method public void onViewCreated(android.view.View, android.os.Bundle);
+ method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
method public void setArguments(android.os.Bundle);
method public void setHasOptionsMenu(boolean);
@@ -3900,6 +3906,15 @@
method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
}
+ public class Presentation extends android.app.Dialog {
+ ctor public Presentation(android.content.Context, android.view.Display);
+ ctor public Presentation(android.content.Context, android.view.Display, int);
+ method public android.view.Display getDisplay();
+ method public android.content.res.Resources getResources();
+ method public void onDisplayChanged();
+ method public void onDisplayRemoved();
+ }
+
public class ProgressDialog extends android.app.AlertDialog {
ctor public ProgressDialog(android.content.Context);
ctor public ProgressDialog(android.content.Context, int);
@@ -4168,6 +4183,7 @@
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
+ field public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9; // 0x9
field public static final int USES_POLICY_EXPIRE_PASSWORD = 6; // 0x6
field public static final int USES_POLICY_FORCE_LOCK = 3; // 0x3
field public static final int USES_POLICY_LIMIT_PASSWORD = 0; // 0x0
@@ -4203,6 +4219,7 @@
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public boolean getCameraDisabled(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public int getKeyguardWidgetsDisabled(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
@@ -4226,6 +4243,7 @@
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
method public void setCameraDisabled(android.content.ComponentName, boolean);
+ method public void setKeyguardWidgetsDisabled(android.content.ComponentName, int);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -4249,6 +4267,8 @@
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+ field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 2147483647; // 0x7fffffff
+ field public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0; // 0x0
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -5267,6 +5287,7 @@
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
method public abstract boolean deleteDatabase(java.lang.String);
@@ -5322,15 +5343,19 @@
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
method public abstract void removeStickyBroadcast(android.content.Intent);
+ method public abstract void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void sendBroadcast(android.content.Intent);
method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void sendStickyBroadcast(android.content.Intent);
+ method public abstract void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public abstract void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public abstract void setTheme(int);
method public abstract deprecated void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method public abstract deprecated void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -5412,6 +5437,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -5459,15 +5485,19 @@
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
method public void removeStickyBroadcast(android.content.Intent);
+ method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
+ method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
method public void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method public void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -5749,6 +5779,7 @@
field public static final java.lang.String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final java.lang.String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+ field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
@@ -6228,6 +6259,7 @@
field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
field public static final int CONFIG_KEYBOARD = 16; // 0x10
field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20
+ field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000
field public static final int CONFIG_LOCALE = 4; // 0x4
field public static final int CONFIG_MCC = 1; // 0x1
field public static final int CONFIG_MNC = 2; // 0x2
@@ -6541,6 +6573,7 @@
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
field public static final java.lang.String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
+ field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
@@ -6829,9 +6862,12 @@
method public int describeContents();
method public int diff(android.content.res.Configuration);
method public boolean equals(android.content.res.Configuration);
+ method public int getLayoutDirection();
method public boolean isLayoutSizeAtLeast(int);
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
+ method public void setLayoutDirection(java.util.Locale);
+ method public void setLocale(java.util.Locale);
method public void setTo(android.content.res.Configuration);
method public void setToDefaults();
method public int updateFrom(android.content.res.Configuration);
@@ -6860,6 +6896,11 @@
field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 64; // 0x40
+ field public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 192; // 0xc0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 128; // 0x80
+ field public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6; // 0x6
+ field public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
field public static final int SCREENLAYOUT_LONG_UNDEFINED = 0; // 0x0
@@ -6870,6 +6911,7 @@
field public static final int SCREENLAYOUT_SIZE_SMALL = 1; // 0x1
field public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_SIZE_XLARGE = 4; // 0x4
+ field public static final int SCREENLAYOUT_UNDEFINED = 0; // 0x0
field public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0; // 0x0
field public static final int SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
@@ -9996,7 +10038,8 @@
package android.hardware.display {
public final class DisplayManager {
- method public android.view.Display getDisplay(int, android.content.Context);
+ method public android.view.Display getDisplay(int);
+ method public android.view.Display[] getDisplays();
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
}
@@ -12606,6 +12649,7 @@
method public static final android.net.NetworkInfo.DetailedState[] values();
enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
enum_constant public static final android.net.NetworkInfo.DetailedState BLOCKED;
+ enum_constant public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTED;
enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTING;
enum_constant public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
@@ -16375,6 +16419,7 @@
method public void finishBroadcast();
method public java.lang.Object getBroadcastCookie(int);
method public E getBroadcastItem(int);
+ method public int getRegisteredCallbackCount();
method public void kill();
method public void onCallbackDied(E);
method public void onCallbackDied(E, java.lang.Object);
@@ -18675,6 +18720,53 @@
field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
}
+ public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+ ctor public Settings.Global();
+ method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
+ method public static float getFloat(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
+ method public static int getInt(android.content.ContentResolver, java.lang.String, int);
+ method public static int getInt(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
+ method public static long getLong(android.content.ContentResolver, java.lang.String, long);
+ method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
+ method public static synchronized java.lang.String getString(android.content.ContentResolver, java.lang.String);
+ method public static android.net.Uri getUriFor(java.lang.String);
+ method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
+ method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
+ method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
+ method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
+ field public static final java.lang.String ADB_ENABLED = "adb_enabled";
+ field public static final java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
+ field public static final java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
+ field public static final java.lang.String AUTO_TIME = "auto_time";
+ field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
+ field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String DATA_ROAMING = "data_roaming";
+ field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+ field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
+ field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
+ field public static final java.lang.String RADIO_CELL = "cell";
+ field public static final java.lang.String RADIO_NFC = "nfc";
+ field public static final java.lang.String RADIO_WIFI = "wifi";
+ field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
+ field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
+ field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+ field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
+ field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+ field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
+ field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
+ field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
+ field public static final java.lang.String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ field public static final java.lang.String WIFI_ON = "wifi_on";
+ field public static final java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+ field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
+ field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
+ field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
+ field public static final java.lang.String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
+ }
+
public static class Settings.NameValueTable implements android.provider.BaseColumns {
ctor public Settings.NameValueTable();
method public static android.net.Uri getUriFor(android.net.Uri, java.lang.String);
@@ -18701,28 +18793,28 @@
method public static final void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
- field public static final java.lang.String ADB_ENABLED = "adb_enabled";
+ field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
field public static final java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
field public static final java.lang.String ANDROID_ID = "android_id";
field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data";
- field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+ field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
field public static final android.net.Uri CONTENT_URI;
- field public static final java.lang.String DATA_ROAMING = "data_roaming";
+ field public static final deprecated java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEFAULT_INPUT_METHOD = "default_input_method";
- field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
- field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+ field public static final deprecated java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
- field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
- field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
+ field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
field public static final java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
field public static final java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
@@ -18738,9 +18830,9 @@
field public static final deprecated java.lang.String TTS_DEFAULT_VARIANT = "tts_default_variant";
field public static final java.lang.String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
field public static final deprecated java.lang.String TTS_USE_DEFAULTS = "tts_use_defaults";
- field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
- field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
- field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+ field public static final deprecated java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+ field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
+ field public static final deprecated java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
@@ -18773,7 +18865,7 @@
method public static int getInt(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
- method public static boolean getShowGTalkServiceStatus(android.content.ContentResolver);
+ method public static deprecated boolean getShowGTalkServiceStatus(android.content.ContentResolver);
method public static synchronized java.lang.String getString(android.content.ContentResolver, java.lang.String);
method public static android.net.Uri getUriFor(java.lang.String);
method public static boolean putConfiguration(android.content.ContentResolver, android.content.res.Configuration);
@@ -18781,18 +18873,18 @@
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
- method public static void setShowGTalkServiceStatus(android.content.ContentResolver, boolean);
+ method public static deprecated void setShowGTalkServiceStatus(android.content.ContentResolver, boolean);
field public static final java.lang.String ACCELEROMETER_ROTATION = "accelerometer_rotation";
field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
- field public static final java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
- field public static final java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
+ field public static final deprecated java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
+ field public static final deprecated java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final java.lang.String ALARM_ALERT = "alarm_alert";
field public static final java.lang.String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
field public static final deprecated java.lang.String ANDROID_ID = "android_id";
field public static final java.lang.String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
field public static final java.lang.String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
- field public static final java.lang.String AUTO_TIME = "auto_time";
- field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
+ field public static final deprecated java.lang.String AUTO_TIME = "auto_time";
+ field public static final deprecated java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_DISCOVERABILITY = "bluetooth_discoverability";
field public static final java.lang.String BLUETOOTH_DISCOVERABILITY_TIMEOUT = "bluetooth_discoverability_timeout";
field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
@@ -18825,10 +18917,10 @@
field public static final deprecated java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
field public static final deprecated java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
field public static final deprecated java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
- field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
- field public static final java.lang.String RADIO_CELL = "cell";
- field public static final java.lang.String RADIO_NFC = "nfc";
- field public static final java.lang.String RADIO_WIFI = "wifi";
+ field public static final deprecated java.lang.String RADIO_BLUETOOTH = "bluetooth";
+ field public static final deprecated java.lang.String RADIO_CELL = "cell";
+ field public static final deprecated java.lang.String RADIO_NFC = "nfc";
+ field public static final deprecated java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String RINGTONE = "ringtone";
field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
@@ -18841,7 +18933,7 @@
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
- field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
+ field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
@@ -18869,10 +18961,10 @@
field public static final deprecated java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
field public static final deprecated java.lang.String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
field public static final deprecated java.lang.String WIFI_ON = "wifi_on";
- field public static final java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
- field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
- field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
- field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
+ field public static final deprecated java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+ field public static final deprecated int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
+ field public static final deprecated int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
+ field public static final deprecated int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final java.lang.String WIFI_STATIC_DNS1 = "wifi_static_dns1";
field public static final java.lang.String WIFI_STATIC_DNS2 = "wifi_static_dns2";
field public static final java.lang.String WIFI_STATIC_GATEWAY = "wifi_static_gateway";
@@ -20438,6 +20530,9 @@
public abstract class CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
method public abstract boolean equals(java.lang.Object);
+ method public abstract int getAsuLevel();
+ method public abstract int getDbm();
+ method public abstract int getLevel();
method public abstract int hashCode();
method public abstract void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -20445,11 +20540,16 @@
public class CellSignalStrengthCdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public boolean equals(java.lang.Object);
+ method public int getAsuLevel();
method public int getCdmaDbm();
method public int getCdmaEcio();
+ method public int getCdmaLevel();
+ method public int getDbm();
method public int getEvdoDbm();
method public int getEvdoEcio();
+ method public int getEvdoLevel();
method public int getEvdoSnr();
+ method public int getLevel();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -20457,6 +20557,9 @@
public class CellSignalStrengthGsm extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public boolean equals(java.lang.Object);
+ method public int getAsuLevel();
+ method public int getDbm();
+ method public int getLevel();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -20464,6 +20567,9 @@
public class CellSignalStrengthLte extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public boolean equals(java.lang.Object);
+ method public int getAsuLevel();
+ method public int getDbm();
+ method public int getLevel();
method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
@@ -21201,6 +21307,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -21247,15 +21354,19 @@
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
method public void removeStickyBroadcast(android.content.Intent);
+ method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
+ method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
method public void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method public void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -22880,6 +22991,7 @@
public class DisplayMetrics {
ctor public DisplayMetrics();
+ method public boolean equals(android.util.DisplayMetrics);
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
field public static final int DENSITY_DEFAULT = 160; // 0xa0
@@ -23428,6 +23540,7 @@
method public int getDisplayId();
method public deprecated int getHeight();
method public void getMetrics(android.util.DisplayMetrics);
+ method public java.lang.String getName();
method public deprecated int getOrientation();
method public deprecated int getPixelFormat();
method public void getRealMetrics(android.util.DisplayMetrics);
@@ -23437,6 +23550,7 @@
method public int getRotation();
method public void getSize(android.graphics.Point);
method public deprecated int getWidth();
+ method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
}
@@ -24427,7 +24541,7 @@
method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
method public void readFromParcel(android.os.Parcel);
method public void release();
- method public void unlockCanvas(android.graphics.Canvas);
+ method public deprecated void unlockCanvas(android.graphics.Canvas);
method public void unlockCanvasAndPost(android.graphics.Canvas);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
@@ -26595,6 +26709,7 @@
public final class InputMethodSubtype implements android.os.Parcelable {
ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean);
+ ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean, int);
method public boolean containsExtraValueKey(java.lang.String);
method public int describeContents();
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
@@ -27645,6 +27760,7 @@
method public void setDropDownVerticalOffset(int);
method public void setDropDownWidth(int);
method public void setListSelection(int);
+ method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener);
method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
method public void setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
method public void setText(java.lang.CharSequence, boolean);
@@ -27653,6 +27769,10 @@
method public void showDropDown();
}
+ public static abstract interface AutoCompleteTextView.OnDismissListener {
+ method public abstract void onDismiss();
+ }
+
public static abstract interface AutoCompleteTextView.Validator {
method public abstract java.lang.CharSequence fixText(java.lang.CharSequence);
method public abstract boolean isValid(java.lang.CharSequence);
@@ -29565,6 +29685,7 @@
public class ViewAnimator extends android.widget.FrameLayout {
ctor public ViewAnimator(android.content.Context);
ctor public ViewAnimator(android.content.Context, android.util.AttributeSet);
+ method public boolean getAnimateFirstView();
method public android.view.View getCurrentView();
method public int getDisplayedChild();
method public android.view.animation.Animation getInAnimation();
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 47d6a02..bcd4588 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,7 +31,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -141,6 +140,8 @@
runToUri(true);
} else if (op.equals("switch-user")) {
runSwitchUser();
+ } else if (op.equals("stop-user")) {
+ runStopUser();
} else {
throw new IllegalArgumentException("Unknown command: " + op);
}
@@ -323,7 +324,6 @@
mUserId = Integer.parseInt(nextArgRequired());
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return null;
}
}
@@ -594,7 +594,6 @@
no_window_animation = true;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -738,7 +737,6 @@
persistent = true;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -752,13 +750,27 @@
}
private void runSwitchUser() throws Exception {
- if (android.os.Process.myUid() != 0) {
- throw new RuntimeException("switchuser can only be run as root");
- }
String user = nextArgRequired();
mAm.switchUser(Integer.parseInt(user));
}
+ private void runStopUser() throws Exception {
+ String user = nextArgRequired();
+ int res = mAm.stopUser(Integer.parseInt(user), null);
+ if (res != ActivityManager.USER_OP_SUCCESS) {
+ String txt = "";
+ switch (res) {
+ case ActivityManager.USER_OP_IS_CURRENT:
+ txt = " (Can't stop current user)";
+ break;
+ case ActivityManager.USER_OP_UNKNOWN_USER:
+ txt = " (Unknown user " + user + ")";
+ break;
+ }
+ System.err.println("Switch failed: " + res + txt);
+ }
+ }
+
class MyActivityController extends IActivityController.Stub {
final String mGdbPort;
@@ -1047,7 +1059,6 @@
gdbPort = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -1065,7 +1076,6 @@
enabled = false;
} else {
System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
- showUsage();
return;
}
@@ -1090,7 +1100,6 @@
int div = size.indexOf('x');
if (div <= 0 || div >= (size.length()-1)) {
System.err.println("Error: bad size " + size);
- showUsage();
return;
}
String mstr = size.substring(0, div);
@@ -1100,7 +1109,6 @@
n = Integer.parseInt(nstr);
} catch (NumberFormatException e) {
System.err.println("Error: bad number " + e);
- showUsage();
return;
}
}
@@ -1139,12 +1147,10 @@
density = Integer.parseInt(densityStr);
} catch (NumberFormatException e) {
System.err.println("Error: bad number " + e);
- showUsage();
return;
}
if (density < 72) {
System.err.println("Error: density must be >= 72");
- showUsage();
return;
}
}
@@ -1175,12 +1181,12 @@
private class IntentReceiver extends IIntentReceiver.Stub {
private boolean mFinished = false;
- public synchronized void performReceive(
- Intent intent, int rc, String data, Bundle ext, boolean ord,
- boolean sticky) {
- String line = "Broadcast completed: result=" + rc;
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
+ String line = "Broadcast completed: result=" + resultCode;
if (data != null) line = line + ", data=\"" + data + "\"";
- if (ext != null) line = line + ", extras: " + ext;
+ if (extras != null) line = line + ", extras: " + extras;
System.out.println(line);
mFinished = true;
notifyAll();
@@ -1345,6 +1351,7 @@
" am to-uri [INTENT]\n" +
" am to-intent-uri [INTENT]\n" +
" am switch-user <USER_ID>\n" +
+ " am stop-user <USER_ID>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -1403,6 +1410,12 @@
"\n" +
"am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
"\n" +
+ "am switch-user: switch to put USER_ID in the foreground, starting" +
+ " execution of that user if it is currently stopped.\n" +
+ "\n" +
+ "am stop-user: stop execution of USER_ID, not allowing it to run any" +
+ " code until a later explicit switch to it.\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 2471a2e..8511735 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -38,6 +38,7 @@
#include <ui/DisplayInfo.h>
#include <ui/FramebufferNativeWindow.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -216,14 +217,16 @@
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
+ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(0, &dinfo);
+ status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
if (status)
return -1;
// create the native surface
- sp<SurfaceControl> control = session()->createSurface(
- 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
+ sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
+ dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
control->setLayer(0x40000000);
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index a52f74a..9e83a67 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -14,6 +14,7 @@
** limitations under the License.
*/
+#include <linux/capability.h>
#include "installd.h"
#include <diskusage/dirsize.h>
@@ -241,7 +242,6 @@
{
char src_data_dir[PKG_PATH_MAX];
char pkg_path[PKG_PATH_MAX];
- char media_path[PATH_MAX];
DIR *d;
struct dirent *de;
struct stat s;
@@ -250,9 +250,6 @@
if (create_persona_path(src_data_dir, src_persona)) {
return -1;
}
- if (create_persona_media_path(media_path, (userid_t) target_persona) == -1) {
- return -1;
- }
d = opendir(src_data_dir);
if (d != NULL) {
@@ -281,10 +278,10 @@
closedir(d);
}
- // ensure /data/media/<user_id> exists
- if (ensure_dir(media_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ if (ensure_media_user_dirs((userid_t) target_persona) == -1) {
return -1;
}
+
return 0;
}
@@ -669,16 +666,16 @@
ALOGE("dexopt cannot open '%s' for output\n", dex_path);
goto fail;
}
- if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
- ALOGE("dexopt cannot chown '%s'\n", dex_path);
- goto fail;
- }
if (fchmod(odex_fd,
S_IRUSR|S_IWUSR|S_IRGRP |
(is_public ? S_IROTH : 0)) < 0) {
ALOGE("dexopt cannot chmod '%s'\n", dex_path);
goto fail;
}
+ if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
+ ALOGE("dexopt cannot chown '%s'\n", dex_path);
+ goto fail;
+ }
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
@@ -694,13 +691,23 @@
ALOGE("setuid(%d) during dexopt\n", uid);
exit(65);
}
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));
- exit(66);
+ exit(67);
}
run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);
- exit(67); /* only get here on exec failure */
+ exit(68); /* only get here on exec failure */
} else {
res = wait_dexopt(pid, apk_path);
if (res != 0) {
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index d51004a..652543fd 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -14,6 +14,9 @@
** limitations under the License.
*/
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
#include "installd.h"
@@ -333,19 +336,16 @@
int initialize_directories() {
int res = -1;
- int version = 0;
- FILE* file;
// Read current filesystem layout version to handle upgrade paths
char version_path[PATH_MAX];
- if (snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path) > PATH_MAX) {
- return -1;
+ snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);
+
+ int oldVersion;
+ if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
+ oldVersion = 0;
}
- file = fopen(version_path, "r");
- if (file != NULL) {
- fscanf(file, "%d", &version);
- fclose(file);
- }
+ int version = oldVersion;
// /data/user
char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
@@ -376,16 +376,12 @@
}
}
- // /data/media/0
- char owner_media_dir[PATH_MAX];
- create_persona_media_path(owner_media_dir, 0);
-
if (version == 0) {
// Introducing multi-user, so migrate /data/media contents into /data/media/0
- ALOGD("Migrating /data/media for multi-user");
+ ALOGD("Upgrading /data/media for multi-user");
// Ensure /data/media
- if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
@@ -402,10 +398,14 @@
}
// Create /data/media again
- if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
+ // /data/media/0
+ char owner_media_dir[PATH_MAX];
+ snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);
+
// Move any owner data into place
if (access(media_tmp_dir, F_OK) == 0) {
if (rename(media_tmp_dir, owner_media_dir) == -1) {
@@ -433,8 +433,7 @@
// /data/media/<user_id>
snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
- if (ensure_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
- ALOGE("Failed to ensure %s: %s", user_media_dir, strerror(errno));
+ if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
}
@@ -445,20 +444,44 @@
version = 1;
}
- // Ensure /data/media/0 is always ready
- if (ensure_dir(owner_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ // /data/media/obb
+ char media_obb_dir[PATH_MAX];
+ snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path);
+
+ if (version == 1) {
+ // Introducing /data/media/obb for sharing OBB across users; migrate
+ // any existing OBB files from owner.
+ ALOGD("Upgrading to shared /data/media/obb");
+
+ // /data/media/0/Android/obb
+ char owner_obb_path[PATH_MAX];
+ snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path);
+
+ // Only move if target doesn't already exist
+ if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) {
+ if (rename(owner_obb_path, media_obb_dir) == -1) {
+ ALOGE("Failed to move OBB from owner: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ version = 2;
+ }
+
+ if (ensure_media_user_dirs(0) == -1) {
+ ALOGE("Failed to setup media for user 0");
+ goto fail;
+ }
+ if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
goto fail;
}
- // Persist our current version
- file = fopen(version_path, "w");
- if (file != NULL) {
- fprintf(file, "%d", version);
- fsync(fileno(file));
- fclose(file);
- } else {
- ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
- goto fail;
+ // Persist layout version if changed
+ if (version != oldVersion) {
+ if (fs_write_atomic_int(version_path, version) == -1) {
+ ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+ goto fail;
+ }
}
// Success!
@@ -471,12 +494,53 @@
return res;
}
+static void drop_privileges() {
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_INSTALL) < 0) {
+ ALOGE("setgid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ if (setuid(AID_INSTALL) < 0) {
+ ALOGE("setuid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
+ capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s, count;
+ ALOGI("installd firing up\n");
+
if (initialize_globals() < 0) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
@@ -487,6 +551,8 @@
exit(1);
}
+ drop_privileges();
+
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 3201427..5b81d2c 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/wait.h>
+#include <cutils/fs.h>
#include <cutils/sockets.h>
#include <cutils/log.h>
#include <cutils/properties.h>
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 80247f1..625a35e 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -991,39 +991,14 @@
return result;
}
-/* Ensure that directory exists with given mode and owners. */
-int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
- // Check if path needs to be created
- struct stat sb;
- if (stat(path, &sb) == -1) {
- if (errno == ENOENT) {
- goto create;
- } else {
- ALOGE("Failed to stat(%s): %s", path, strerror(errno));
- return -1;
- }
- }
+/* Ensure that /data/media directories are prepared for given user. */
+int ensure_media_user_dirs(userid_t userid) {
+ char media_user_path[PATH_MAX];
+ char path[PATH_MAX];
- // Exists, verify status
- if (sb.st_mode == mode || sb.st_uid == uid || sb.st_gid == gid) {
- return 0;
- } else {
- goto fixup;
- }
-
-create:
- if (mkdir(path, mode) == -1) {
- ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
- return -1;
- }
-
-fixup:
- if (chown(path, uid, gid) == -1) {
- ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
- return -1;
- }
- if (chmod(path, mode) == -1) {
- ALOGE("Failed to chown(%s, %d): %s", path, mode, strerror(errno));
+ // Ensure /data/media/<userid> exists
+ create_persona_media_path(media_user_path, userid);
+ if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
return -1;
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 8cc4e69..36bf38a 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -178,11 +178,6 @@
return;
}
- if ("list-users".equals(op)) {
- runListUsers();
- return;
- }
-
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
@@ -222,7 +217,6 @@
String type = nextArg();
if (type == null) {
System.err.println("Error: didn't specify type of data to list");
- showUsage();
return;
}
if ("package".equals(type) || "packages".equals(type)) {
@@ -241,7 +235,6 @@
runListUsers();
} else {
System.err.println("Error: unknown list type '" + type + "'");
- showUsage();
}
}
@@ -276,13 +269,11 @@
getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
} catch (RuntimeException ex) {
System.err.println("Error: " + ex.toString());
- showUsage();
return;
}
@@ -431,13 +422,11 @@
targetPackage = opt;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
} catch (RuntimeException ex) {
System.err.println("Error: " + ex.toString());
- showUsage();
return;
}
@@ -529,7 +518,6 @@
dangerousOnly = true;
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -678,7 +666,6 @@
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
- showUsage();
return;
}
displayPackageFilePath(pkg);
@@ -736,20 +723,17 @@
String arg = nextArg();
if (arg == null) {
System.err.println("Error: no install location specified.");
- showUsage();
return;
}
try {
loc = Integer.parseInt(arg);
} catch (NumberFormatException e) {
System.err.println("Error: install location has to be a number.");
- showUsage();
return;
}
try {
if (!mPm.setInstallLocation(loc)) {
System.err.println("Error: install location has to be a number.");
- showUsage();
}
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -800,7 +784,6 @@
installerPackageName = nextOptionData();
if (installerPackageName == null) {
System.err.println("Error: no value specified for -i");
- showUsage();
return;
}
} else if (opt.equals("-t")) {
@@ -817,61 +800,52 @@
algo = nextOptionData();
if (algo == null) {
System.err.println("Error: must supply argument for --algo");
- showUsage();
return;
}
} else if (opt.equals("--iv")) {
iv = hexToBytes(nextOptionData());
if (iv == null) {
System.err.println("Error: must supply argument for --iv");
- showUsage();
return;
}
} else if (opt.equals("--key")) {
key = hexToBytes(nextOptionData());
if (key == null) {
System.err.println("Error: must supply argument for --key");
- showUsage();
return;
}
} else if (opt.equals("--macalgo")) {
macAlgo = nextOptionData();
if (macAlgo == null) {
System.err.println("Error: must supply argument for --macalgo");
- showUsage();
return;
}
} else if (opt.equals("--mackey")) {
macKey = hexToBytes(nextOptionData());
if (macKey == null) {
System.err.println("Error: must supply argument for --mackey");
- showUsage();
return;
}
} else if (opt.equals("--tag")) {
tag = hexToBytes(nextOptionData());
if (tag == null) {
System.err.println("Error: must supply argument for --tag");
- showUsage();
return;
}
} else if (opt.equals("--originating-uri")) {
originatingUriString = nextOptionData();
if (originatingUriString == null) {
System.err.println("Error: must supply argument for --originating-uri");
- showUsage();
return;
}
} else if (opt.equals("--referrer")) {
referrer = nextOptionData();
if (referrer == null) {
System.err.println("Error: must supply argument for --referrer");
- showUsage();
return;
}
} else {
System.err.println("Error: Unknown option: " + opt);
- showUsage();
return;
}
}
@@ -881,7 +855,6 @@
|| tag != null) {
if (algo == null || iv == null || key == null) {
System.err.println("Error: all of --algo, --iv, and --key must be specified");
- showUsage();
return;
}
@@ -889,7 +862,6 @@
if (macAlgo == null || macKey == null || tag == null) {
System.err.println("Error: all of --macalgo, --mackey, and --tag must "
+ "be specified");
- showUsage();
return;
}
}
@@ -938,7 +910,6 @@
apkURI = Uri.fromFile(new File(apkFilePath));
} else {
System.err.println("Error: no package specified");
- showUsage();
return;
}
@@ -1012,23 +983,16 @@
}
public void runCreateUser() {
- // Need to be run as root
- if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: create-user must be run as root");
- return;
- }
String name;
String arg = nextArg();
if (arg == null) {
System.err.println("Error: no user name specified.");
- showUsage();
return;
}
name = arg;
try {
if (mUm.createUser(name, 0) == null) {
System.err.println("Error: couldn't create User.");
- showUsage();
}
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -1038,29 +1002,21 @@
}
public void runRemoveUser() {
- // Need to be run as root
- if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: remove-user must be run as root");
- return;
- }
int userId;
String arg = nextArg();
if (arg == null) {
System.err.println("Error: no user id specified.");
- showUsage();
return;
}
try {
userId = Integer.parseInt(arg);
} catch (NumberFormatException e) {
- System.err.println("Error: user id has to be a number.");
- showUsage();
+ System.err.println("Error: user id '" + arg + "' is not a number.");
return;
}
try {
if (!mUm.removeUser(userId)) {
- System.err.println("Error: couldn't remove user.");
- showUsage();
+ System.err.println("Error: couldn't remove user #" + userId + ".");
}
} catch (RemoteException e) {
System.err.println(e.toString());
@@ -1069,11 +1025,6 @@
}
public void runListUsers() {
- // Need to be run as root
- if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: list-users must be run as root");
- return;
- }
try {
List<UserInfo> users = mUm.getUsers();
if (users == null) {
@@ -1169,8 +1120,8 @@
ClearDataObserver obs = new ClearDataObserver();
try {
- if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
- Binder.getOrigCallingUser())) {
+ // XXX TO DO: add user arg
+ if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, 0)) {
System.err.println("Failed");
}
@@ -1521,6 +1472,8 @@
System.err.println("");
System.err.println("pm list features: prints all features of the system.");
System.err.println("");
+ System.err.println("pm list users: prints all users on the system.");
+ System.err.println("");
System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
System.err.println("");
System.err.println("pm install: installs a package to the system. Options:");
@@ -1557,5 +1510,11 @@
System.err.println(" 2 [external]: Install on external media");
System.err.println("");
System.err.println("pm trim-caches: trim cache files to reach the given free space.");
+ System.err.println("");
+ System.err.println("pm create-user: create a new user with the given USER_NAME,");
+ System.err.println(" printing the new user identifier of the user.");
+ System.err.println("");
+ System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,");
+ System.err.println(" deleting all data associated with that user");
}
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 46e41e3..a1ea81a 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -25,6 +25,7 @@
#include <binder/IMemory.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/ISurfaceComposer.h>
#include <SkImageEncoder.h>
#include <SkBitmap.h>
@@ -33,15 +34,18 @@
using namespace android;
+static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
+
static void usage(const char* pname)
{
fprintf(stderr,
- "usage: %s [-hp] [FILENAME]\n"
+ "usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
" -p: save the file as a png.\n"
+ " -d: specify the display id to capture, default %d.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
- pname
+ pname, DEFAULT_DISPLAY_ID
);
}
@@ -87,12 +91,16 @@
{
const char* pname = argv[0];
bool png = false;
+ int32_t displayId = DEFAULT_DISPLAY_ID;
int c;
- while ((c = getopt(argc, argv, "ph")) != -1) {
+ while ((c = getopt(argc, argv, "phd:")) != -1) {
switch (c) {
case 'p':
png = true;
break;
+ case 'd':
+ displayId = atoi(optarg);
+ break;
case '?':
case 'h':
usage(pname);
@@ -131,7 +139,8 @@
size_t size = 0;
ScreenshotClient screenshot;
- if (screenshot.update() == NO_ERROR) {
+ sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
+ if (display != NULL && screenshot.update(display) == NO_ERROR) {
base = screenshot.getPixels();
w = screenshot.getWidth();
h = screenshot.getHeight();
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 10ea0fe..75a4f83 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -85,6 +85,11 @@
public static final int FEEDBACK_GENERIC = 0x0000010;
/**
+ * Denotes braille feedback.
+ */
+ public static final int FEEDBACK_BRAILLE = 0x0000020;
+
+ /**
* Mask for all feedback types.
*
* @see #FEEDBACK_SPOKEN
@@ -92,6 +97,7 @@
* @see #FEEDBACK_AUDIBLE
* @see #FEEDBACK_VISUAL
* @see #FEEDBACK_GENERIC
+ * @see #FEEDBACK_BRAILLE
*/
public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
@@ -186,6 +192,7 @@
* @see #FEEDBACK_HAPTIC
* @see #FEEDBACK_SPOKEN
* @see #FEEDBACK_VISUAL
+ * @see #FEEDBACK_BRAILLE
*/
public int feedbackType;
@@ -591,6 +598,12 @@
}
builder.append("FEEDBACK_VISUAL");
break;
+ case FEEDBACK_BRAILLE:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
+ builder.append("FEEDBACK_BRAILLE");
+ break;
}
}
builder.append("]");
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 7a9f285..9caf84f 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1052,7 +1052,7 @@
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
- UserAccounts accounts = getUserAccountsForCaller();
+ final UserAccounts accounts = getUserAccountsForCaller();
AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
mAuthenticatorCache.getServiceInfo(
AuthenticatorDescription.newKey(account.type));
@@ -1141,7 +1141,7 @@
if (intent != null && notifyOnAuthFailure && !customTokens) {
doNotification(mAccounts,
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
- intent);
+ intent, accounts.userId);
}
}
super.onResult(result);
@@ -1152,7 +1152,8 @@
}
}
- private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
+ private void createNoCredentialsPermissionNotification(Account account, Intent intent,
+ int userId) {
int uid = intent.getIntExtra(
GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
String authTokenType = intent.getStringExtra(
@@ -1172,9 +1173,10 @@
title = titleAndSubtitle.substring(0, index);
subtitle = titleAndSubtitle.substring(index + 1);
}
- n.setLatestEventInfo(mContext,
- title, subtitle,
- PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ n.setLatestEventInfo(mContext, title, subtitle,
+ PendingIntent.getActivityAsUser(mContext, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT,
+ null, new UserHandle(userId)));
installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
}
@@ -1879,7 +1881,7 @@
private static String getDatabaseName(int userId) {
File systemDir = Environment.getSystemSecureDirectory();
- File databaseFile = new File(systemDir, "users/" + userId + "/" + DATABASE_NAME);
+ File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
// Make sure the new file doesn't already exist. A dummy file could have been
@@ -1888,7 +1890,7 @@
File oldFile = new File(systemDir, DATABASE_NAME);
if (oldFile.exists() && !databaseFile.exists()) {
// Check for use directory; create if it doesn't exist, else renameTo will fail
- File userDir = new File(systemDir, "users/" + userId);
+ File userDir = Environment.getUserSystemDirectory(userId);
if (!userDir.exists()) {
if (!userDir.mkdirs()) {
throw new IllegalStateException("User dir cannot be created: " + userDir);
@@ -2083,7 +2085,7 @@
}
private void doNotification(UserAccounts accounts, Account account, CharSequence message,
- Intent intent) {
+ Intent intent, int userId) {
long identityToken = clearCallingIdentity();
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2093,7 +2095,7 @@
if (intent.getComponent() != null &&
GrantCredentialsPermissionActivity.class.getName().equals(
intent.getComponent().getClassName())) {
- createNoCredentialsPermissionNotification(account, intent);
+ createNoCredentialsPermissionNotification(account, intent, userId);
} else {
final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
intent.addCategory(String.valueOf(notificationId));
@@ -2103,8 +2105,9 @@
mContext.getText(R.string.notification_title).toString();
n.setLatestEventInfo(mContext,
String.format(notificationTitleFormat, account.name),
- message, PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ message, PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
+ null, new UserHandle(userId)));
installNotification(notificationId, n);
}
} finally {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d5580b7..05b04dc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -653,8 +653,9 @@
/** Start of user-defined activity results. */
public static final int RESULT_FIRST_USER = 1;
+ static final String FRAGMENTS_TAG = "android:fragments";
+
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
- private static final String FRAGMENTS_TAG = "android:fragments";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
@@ -697,7 +698,7 @@
Object activity;
HashMap<String, Object> children;
ArrayList<Fragment> fragments;
- SparseArray<LoaderManagerImpl> loaders;
+ HashMap<String, LoaderManagerImpl> loaders;
}
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
@@ -715,8 +716,14 @@
private int mTitleColor = 0;
final FragmentManagerImpl mFragments = new FragmentManagerImpl();
+ final FragmentContainer mContainer = new FragmentContainer() {
+ @Override
+ public View findViewById(int id) {
+ return Activity.this.findViewById(id);
+ }
+ };
- SparseArray<LoaderManagerImpl> mAllLoaderManagers;
+ HashMap<String, LoaderManagerImpl> mAllLoaderManagers;
LoaderManagerImpl mLoaderManager;
private static final class ManagedCursor {
@@ -744,6 +751,7 @@
protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
+ @SuppressWarnings("unused")
private final Object mInstanceTracker = StrictMode.trackActivity(this);
private Thread mUiThread;
@@ -808,19 +816,19 @@
return mLoaderManager;
}
mCheckedForLoaderManager = true;
- mLoaderManager = getLoaderManager(-1, mLoadersStarted, true);
+ mLoaderManager = getLoaderManager(null, mLoadersStarted, true);
return mLoaderManager;
}
- LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) {
+ LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
if (mAllLoaderManagers == null) {
- mAllLoaderManagers = new SparseArray<LoaderManagerImpl>();
+ mAllLoaderManagers = new HashMap<String, LoaderManagerImpl>();
}
- LoaderManagerImpl lm = mAllLoaderManagers.get(index);
+ LoaderManagerImpl lm = mAllLoaderManagers.get(who);
if (lm == null) {
if (create) {
- lm = new LoaderManagerImpl(this, started);
- mAllLoaderManagers.put(index, lm);
+ lm = new LoaderManagerImpl(who, this, started);
+ mAllLoaderManagers.put(who, lm);
}
} else {
lm.updateActivity(this);
@@ -1025,7 +1033,7 @@
if (mLoaderManager != null) {
mLoaderManager.doStart();
} else if (!mCheckedForLoaderManager) {
- mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
+ mLoaderManager = getLoaderManager(null, mLoadersStarted, false);
}
mCheckedForLoaderManager = true;
}
@@ -1601,13 +1609,17 @@
if (mAllLoaderManagers != null) {
// prune out any loader managers that were already stopped and so
// have nothing useful to retain.
- for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
- LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i);
- if (lm.mRetaining) {
- retainLoaders = true;
- } else {
- lm.doDestroy();
- mAllLoaderManagers.removeAt(i);
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[mAllLoaderManagers.size()];
+ mAllLoaderManagers.values().toArray(loaders);
+ if (loaders != null) {
+ for (int i=0; i<loaders.length; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ if (lm.mRetaining) {
+ retainLoaders = true;
+ } else {
+ lm.doDestroy();
+ mAllLoaderManagers.remove(lm.mWho);
+ }
}
}
}
@@ -1643,13 +1655,13 @@
return mFragments;
}
- void invalidateFragmentIndex(int index) {
+ void invalidateFragment(String who) {
//Log.v(TAG, "invalidateFragmentIndex: index=" + index);
if (mAllLoaderManagers != null) {
- LoaderManagerImpl lm = mAllLoaderManagers.get(index);
+ LoaderManagerImpl lm = mAllLoaderManagers.get(who);
if (lm != null && !lm.mRetaining) {
lm.doDestroy();
- mAllLoaderManagers.remove(index);
+ mAllLoaderManagers.remove(who);
}
}
}
@@ -4278,7 +4290,8 @@
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
mParent == null ? mToken : mParent.mToken,
- mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null);
+ mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
+ UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
@@ -4738,6 +4751,10 @@
* @param args additional arguments to the dump request.
*/
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ dumpInner(prefix, fd, writer, args);
+ }
+
+ void dumpInner(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
writer.print(prefix); writer.print("Local Activity ");
writer.print(Integer.toHexString(System.identityHashCode(this)));
writer.println(" State:");
@@ -5018,7 +5035,7 @@
Configuration config) {
attachBaseContext(context);
- mFragments.attachActivity(this);
+ mFragments.attachActivity(this, mContainer, null);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
@@ -5079,10 +5096,14 @@
}
mFragments.dispatchStart();
if (mAllLoaderManagers != null) {
- for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
- LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i);
- lm.finishRetain();
- lm.doReportStart();
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[mAllLoaderManagers.size()];
+ mAllLoaderManagers.values().toArray(loaders);
+ if (loaders != null) {
+ for (int i=0; i<loaders.length; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ lm.finishRetain();
+ lm.doReportStart();
+ }
}
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e644db4..cd22aad 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -27,10 +27,12 @@
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -210,6 +212,15 @@
*/
public static final int INTENT_SENDER_SERVICE = 4;
+ /** @hide User operation call: success! */
+ public static final int USER_OP_SUCCESS = 0;
+
+ /** @hide User operation call: given user id is not known. */
+ public static final int USER_OP_UNKNOWN_USER = -1;
+
+ /** @hide User operation call: given user id is the current user, can't be stopped. */
+ public static final int USER_OP_IS_CURRENT = -2;
+
/*package*/ ActivityManager(Context context, Handler handler) {
mContext = context;
mHandler = handler;
@@ -376,7 +387,8 @@
return true;
}
- Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ Display display = DisplayManagerGlobal.getInstance().getRealDisplay(
+ Display.DEFAULT_DISPLAY);
Point p = new Point();
display.getRealSize(p);
int pixels = p.x * p.y;
@@ -1214,7 +1226,7 @@
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
- observer, Binder.getOrigCallingUser());
+ observer, UserHandle.myUserId());
} catch (RemoteException e) {
return false;
}
@@ -1891,6 +1903,31 @@
return PackageManager.PERMISSION_DENIED;
}
+ /** @hide */
+ public static int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, boolean requireFull, String name, String callerPackage) {
+ if (UserHandle.getUserId(callingUid) == userId) {
+ return userId;
+ }
+ try {
+ return ActivityManagerNative.getDefault().handleIncomingUser(callingPid,
+ callingUid, userId, allowAll, requireFull, name, callerPackage);
+ } catch (RemoteException e) {
+ throw new SecurityException("Failed calling activity manager", e);
+ }
+ }
+
+ /** @hide */
+ public static int getCurrentUser() {
+ UserInfo ui;
+ try {
+ ui = ActivityManagerNative.getDefault().getCurrentUser();
+ return ui != null ? ui.id : 0;
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
/**
* Returns the usage statistics of each installed package.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index adc9434..eed9254 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -89,11 +89,11 @@
* Convenience for sending a sticky broadcast. For internal use only.
* If you don't care about permission, use null.
*/
- static public void broadcastStickyIntent(Intent intent, String permission) {
+ static public void broadcastStickyIntent(Intent intent, String permission, int userId) {
try {
getDefault().broadcastIntent(
null, intent, null, null, Activity.RESULT_OK, null, null,
- null /*permission*/, false, true, Binder.getOrigCallingUser());
+ null /*permission*/, false, true, userId);
} catch (RemoteException ex) {
}
}
@@ -199,8 +199,9 @@
Configuration config = Configuration.CREATOR.createFromParcel(data);
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
int result = startActivityWithConfig(app, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, config, options);
+ resultTo, resultWho, requestCode, startFlags, config, options, userId);
reply.writeNoException();
reply.writeInt(result);
return true;
@@ -299,7 +300,8 @@
= b != null ? IIntentReceiver.Stub.asInterface(b) : null;
IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
String perm = data.readString();
- Intent intent = registerReceiver(app, packageName, rec, filter, perm);
+ int userId = data.readInt();
+ Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
reply.writeNoException();
if (intent != null) {
reply.writeInt(1);
@@ -897,9 +899,10 @@
int fl = data.readInt();
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
IIntentSender res = getIntentSender(type, packageName, token,
resultWho, requestCode, requestIntents,
- requestResolvedTypes, fl, options);
+ requestResolvedTypes, fl, options, userId);
reply.writeNoException();
reply.writeStrongBinder(res != null ? res.asBinder() : null);
return true;
@@ -934,6 +937,22 @@
return true;
}
+ case HANDLE_INCOMING_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int callingPid = data.readInt();
+ int callingUid = data.readInt();
+ int userId = data.readInt();
+ boolean allowAll = data.readInt() != 0 ;
+ boolean requireFull = data.readInt() != 0;
+ String name = data.readString();
+ String callerPackage = data.readString();
+ int res = handleIncomingUser(callingPid, callingUid, userId, allowAll,
+ requireFull, name, callerPackage);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
case SET_PROCESS_LIMIT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int max = data.readInt();
@@ -1304,25 +1323,6 @@
return true;
}
- case START_ACTIVITY_IN_PACKAGE_TRANSACTION:
- {
- data.enforceInterface(IActivityManager.descriptor);
- int uid = data.readInt();
- Intent intent = Intent.CREATOR.createFromParcel(data);
- String resolvedType = data.readString();
- IBinder resultTo = data.readStrongBinder();
- String resultWho = data.readString();
- int requestCode = data.readInt();
- int startFlags = data.readInt();
- Bundle options = data.readInt() != 0
- ? Bundle.CREATOR.createFromParcel(data) : null;
- int result = startActivityInPackage(uid, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, options);
- reply.writeNoException();
- reply.writeInt(result);
- return true;
- }
-
case KILL_APPLICATION_WITH_UID_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -1489,22 +1489,6 @@
return true;
}
- case START_ACTIVITIES_IN_PACKAGE_TRANSACTION:
- {
- data.enforceInterface(IActivityManager.descriptor);
- int uid = data.readInt();
- Intent[] intents = data.createTypedArray(Intent.CREATOR);
- String[] resolvedTypes = data.createStringArray();
- IBinder resultTo = data.readStrongBinder();
- Bundle options = data.readInt() != 0
- ? Bundle.CREATOR.createFromParcel(data) : null;
- int result = startActivitiesInPackage(uid, intents, resolvedTypes,
- resultTo, options);
- reply.writeNoException();
- reply.writeInt(result);
- return true;
- }
-
case START_ACTIVITIES_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -1570,6 +1554,17 @@
return true;
}
+ case STOP_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int userid = data.readInt();
+ IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
+ data.readStrongBinder());
+ int result = stopUser(userid, callback);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case GET_CURRENT_USER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
UserInfo userInfo = getCurrentUser();
@@ -1866,7 +1861,7 @@
public int startActivityWithConfig(IApplicationThread caller, Intent intent,
String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int startFlags, Configuration config,
- Bundle options) throws RemoteException {
+ Bundle options, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -1884,6 +1879,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(userId);
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
@@ -2004,7 +2000,7 @@
}
public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
- IntentFilter filter, String perm) throws RemoteException
+ IntentFilter filter, String perm, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2014,6 +2010,7 @@
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
+ data.writeInt(userId);
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
reply.readException();
Intent intent = null;
@@ -2829,7 +2826,7 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
- Bundle options) throws RemoteException {
+ Bundle options, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -2852,6 +2849,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(userId);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
IIntentSender res = IIntentSender.Stub.asInterface(
@@ -2894,6 +2892,25 @@
reply.recycle();
return res;
}
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(callingPid);
+ data.writeInt(callingUid);
+ data.writeInt(userId);
+ data.writeInt(allowAll ? 1 : 0);
+ data.writeInt(requireFull ? 1 : 0);
+ data.writeString(name);
+ data.writeString(callerPackage);
+ mRemote.transact(HANDLE_INCOMING_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
public void setProcessLimit(int max) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -3349,34 +3366,6 @@
data.recycle();
}
- public int startActivityInPackage(int uid,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeInt(uid);
- intent.writeToParcel(data, 0);
- data.writeString(resolvedType);
- data.writeStrongBinder(resultTo);
- data.writeString(resultWho);
- data.writeInt(requestCode);
- data.writeInt(startFlags);
- if (options != null) {
- data.writeInt(1);
- options.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(START_ACTIVITY_IN_PACKAGE_TRANSACTION, data, reply, 0);
- reply.readException();
- int result = reply.readInt();
- reply.recycle();
- data.recycle();
- return result;
- }
-
public void killApplicationWithUid(String pkg, int uid) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -3644,30 +3633,6 @@
return result;
}
- public int startActivitiesInPackage(int uid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeInt(uid);
- data.writeTypedArray(intents, 0);
- data.writeStringArray(resolvedTypes);
- data.writeStrongBinder(resultTo);
- if (options != null) {
- data.writeInt(1);
- options.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(START_ACTIVITIES_IN_PACKAGE_TRANSACTION, data, reply, 0);
- reply.readException();
- int result = reply.readInt();
- reply.recycle();
- data.recycle();
- return result;
- }
-
public int getFrontActivityScreenCompatMode() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -3756,11 +3721,25 @@
return result;
}
+ public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(userid);
+ data.writeStrongInterface(callback);
+ mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
public UserInfo getCurrentUser() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+ mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
reply.readException();
UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b8e16c5..97dcec0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -43,6 +43,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.net.IConnectivityManager;
import android.net.Proxy;
import android.net.ProxyProperties;
@@ -202,7 +203,7 @@
= new HashMap<String, WeakReference<LoadedApk>>();
final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
= new HashMap<String, WeakReference<LoadedApk>>();
- final HashMap<CompatibilityInfo, DisplayMetrics> mDisplayMetrics
+ final HashMap<CompatibilityInfo, DisplayMetrics> mDefaultDisplayMetrics
= new HashMap<CompatibilityInfo, DisplayMetrics>();
final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
= new HashMap<ResourcesKey, WeakReference<Resources> >();
@@ -319,8 +320,9 @@
static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky, IBinder token) {
- super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+ boolean ordered, boolean sticky, IBinder token, int sendingUser) {
+ super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky,
+ token, sendingUser);
this.intent = intent;
}
@@ -612,9 +614,9 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
- boolean sync) {
+ boolean sync, int sendingUser) {
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
- sync, false, mAppThread.asBinder());
+ sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
queueOrSendMessage(H.RECEIVER, r);
@@ -773,8 +775,9 @@
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky) throws RemoteException {
- receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
+ boolean sticky, int sendingUser) throws RemoteException {
+ receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
+ sticky, sendingUser);
}
public void scheduleLowMemory() {
@@ -1474,12 +1477,14 @@
private static class ResourcesKey {
final private String mResDir;
+ final private int mDisplayId;
final private Configuration mOverrideConfiguration;
final private float mScale;
final private int mHash;
- ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
+ ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, float scale) {
mResDir = resDir;
+ mDisplayId = displayId;
if (overrideConfiguration != null) {
if (Configuration.EMPTY.equals(overrideConfiguration)) {
overrideConfiguration = null;
@@ -1489,6 +1494,7 @@
mScale = scale;
int hash = 17;
hash = 31 * hash + mResDir.hashCode();
+ hash = 31 * hash + mDisplayId;
hash = 31 * hash + (mOverrideConfiguration != null
? mOverrideConfiguration.hashCode() : 0);
hash = 31 * hash + Float.floatToIntBits(mScale);
@@ -1509,6 +1515,9 @@
if (!mResDir.equals(peer.mResDir)) {
return false;
}
+ if (mDisplayId != peer.mDisplayId) {
+ return false;
+ }
if (mOverrideConfiguration != peer.mOverrideConfiguration) {
if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
return false;
@@ -1551,28 +1560,32 @@
return sPackageManager;
}
- DisplayMetrics getDisplayMetricsLocked(CompatibilityInfo ci, boolean forceUpdate) {
- DisplayMetrics dm = mDisplayMetrics.get(ci);
- if (dm != null && !forceUpdate) {
+ private void flushDisplayMetricsLocked() {
+ mDefaultDisplayMetrics.clear();
+ }
+
+ DisplayMetrics getDisplayMetricsLocked(int displayId, CompatibilityInfo ci) {
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(ci) : null;
+ if (dm != null) {
return dm;
}
+ dm = new DisplayMetrics();
- DisplayManager displayManager = DisplayManager.getInstance();
+ DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
if (displayManager == null) {
// may be null early in system startup
- dm = new DisplayMetrics();
dm.setToDefaults();
return dm;
}
- if (dm == null) {
- dm = new DisplayMetrics();
- mDisplayMetrics.put(ci, dm);
+ if (isDefaultDisplay) {
+ mDefaultDisplayMetrics.put(ci, dm);
}
CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
cih.set(ci);
- Display d = displayManager.getCompatibleDisplay(Display.DEFAULT_DISPLAY, cih);
+ Display d = displayManager.getCompatibleDisplay(displayId, cih);
d.getMetrics(dm);
//Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
// + metrics.heightPixels + " den=" + metrics.density
@@ -1601,9 +1614,11 @@
* @param compInfo the compability info. It will use the default compatibility info when it's
* null.
*/
- Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir,
+ int displayId, Configuration overrideConfiguration,
CompatibilityInfo compInfo) {
- ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
+ ResourcesKey key = new ResourcesKey(resDir,
+ displayId, overrideConfiguration,
compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
@@ -1635,15 +1650,21 @@
}
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
- DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
+ DisplayMetrics dm = getDisplayMetricsLocked(displayId, null);
Configuration config;
- if (key.mOverrideConfiguration != null) {
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ if (!isDefaultDisplay || key.mOverrideConfiguration != null) {
config = new Configuration(getConfiguration());
- config.updateFrom(key.mOverrideConfiguration);
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
+ }
+ if (key.mOverrideConfiguration != null) {
+ config.updateFrom(key.mOverrideConfiguration);
+ }
} else {
config = getConfiguration();
}
- r = new Resources(assets, metrics, config, compInfo);
+ r = new Resources(assets, dm, config, compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
@@ -1669,9 +1690,10 @@
/**
* Creates the top level resources for the given package.
*/
- Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir,
+ int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return getTopLevelResources(resDir, overrideConfiguration,
+ return getTopLevelResources(resDir, displayId, overrideConfiguration,
pkgInfo.mCompatibilityInfo.get());
}
@@ -1843,7 +1865,8 @@
context.init(info, null, this);
context.getResources().updateConfiguration(
getConfiguration(), getDisplayMetricsLocked(
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, false));
+ Display.DEFAULT_DISPLAY,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
mSystemContext = context;
//Slog.i(TAG, "Created system resources " + context.getResources()
// + ": " + context.getResources().getConfiguration());
@@ -3706,7 +3729,9 @@
return false;
}
int changes = mResConfiguration.updateFrom(config);
- DisplayMetrics dm = getDisplayMetricsLocked(null, true);
+ flushDisplayMetricsLocked();
+ DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(
+ Display.DEFAULT_DISPLAY, null);
if (compat != null && (mResCompatibilityInfo == null ||
!mResCompatibilityInfo.equals(compat))) {
@@ -3721,7 +3746,7 @@
Locale.setDefault(config.locale);
}
- Resources.updateSystemConfiguration(config, dm, compat);
+ Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -3736,13 +3761,22 @@
if (r != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
- Configuration override = entry.getKey().mOverrideConfiguration;
- if (override != null) {
+ int displayId = entry.getKey().mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ Configuration overrideConfig = entry.getKey().mOverrideConfiguration;
+ if (!isDefaultDisplay || overrideConfig != null) {
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
tmpConfig.setTo(config);
- tmpConfig.updateFrom(override);
+ if (!isDefaultDisplay) {
+ dm = getDisplayMetricsLocked(displayId, null);
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
+ }
+ if (overrideConfig != null) {
+ tmpConfig.updateFrom(overrideConfig);
+ }
r.updateConfiguration(tmpConfig, dm, compat);
} else {
r.updateConfiguration(config, dm, compat);
@@ -3758,6 +3792,22 @@
return changes != 0;
}
+ final void applyNonDefaultDisplayMetricsToConfigurationLocked(
+ DisplayMetrics dm, Configuration config) {
+ config.screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE
+ | Configuration.SCREENLAYOUT_LONG_NO;
+ config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+ config.orientation = (dm.widthPixels >= dm.heightPixels) ?
+ Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ config.densityDpi = dm.densityDpi;
+ config.screenWidthDp = (int)(dm.widthPixels / dm.density);
+ config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+ config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
+ config.compatScreenWidthDp = config.screenWidthDp;
+ config.compatScreenHeightDp = config.screenHeightDp;
+ config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
+ }
+
final Configuration applyCompatConfiguration(int displayDensity) {
Configuration config = mConfiguration;
if (mCompatConfiguration == null) {
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 7809e73..6ab2bd1 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -110,8 +110,9 @@
this(context, theme, true);
}
- AlertDialog(Context context, int theme, boolean createContextWrapper) {
- super(context, resolveDialogTheme(context, theme), createContextWrapper);
+ AlertDialog(Context context, int theme, boolean createThemeContextWrapper) {
+ super(context, resolveDialogTheme(context, theme), createThemeContextWrapper);
+
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0f10c4f..c4f1371 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -54,6 +54,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.Display;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -502,20 +503,28 @@
}
}
+ /**
+ * @hide
+ */
@Override
- public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) {
try {
return mPM.queryIntentReceivers(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserHandle.myUserId());
+ userId);
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
@Override
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
+ return queryBroadcastReceivers(intent, flags, UserHandle.myUserId());
+ }
+
+ @Override
public ResolveInfo resolveService(Intent intent, int flags) {
try {
return mPM.resolveService(
@@ -714,8 +723,8 @@
return mContext.mMainThread.getSystemContext().getResources();
}
Resources r = mContext.mMainThread.getTopLevelResources(
- app.uid == Process.myUid() ? app.sourceDir
- : app.publicSourceDir, null, mContext.mPackageInfo);
+ app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
+ Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
if (r != null) {
return r;
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 8e6278d..63aa5f9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -193,8 +193,9 @@
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
boolean sync = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
- resultExtras, sync);
+ resultExtras, sync, sendingUser);
return true;
}
@@ -378,8 +379,9 @@
Bundle extras = data.readBundle();
boolean ordered = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered, sticky);
+ resultCode, dataStr, extras, ordered, sticky, sendingUser);
return true;
}
@@ -755,7 +757,7 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
- Bundle map, boolean sync) throws RemoteException {
+ Bundle map, boolean sync, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
@@ -765,6 +767,7 @@
data.writeString(resultData);
data.writeBundle(map);
data.writeInt(sync ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -991,8 +994,8 @@
}
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException {
+ int resultCode, String dataStr, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(receiver.asBinder());
@@ -1002,6 +1005,7 @@
data.writeBundle(extras);
data.writeInt(ordered ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 32086d7..65ea6a0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -138,6 +138,17 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new ReceiverCallNotAllowedException(
+ "IntentReceiver components are not allowed to register to receive intents");
+ //ex.fillInStackTrace();
+ //Log.e("IntentReceiver", ex.getMessage(), ex);
+ //return mContext.registerReceiver(receiver, filter, broadcastPermission,
+ // scheduler);
+ }
+
+ @Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException(
"IntentReceiver components are not allowed to bind to services");
@@ -168,6 +179,7 @@
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
+ private Display mDisplay; // may be null if default display
private Context mReceiverRestrictedContext = null;
private boolean mRestricted;
@@ -349,10 +361,11 @@
return InputManager.getInstance();
}});
- registerService(DISPLAY_SERVICE, new StaticServiceFetcher() {
- public Object createStaticService() {
- return DisplayManager.getInstance();
- }});
+ registerService(DISPLAY_SERVICE, new ServiceFetcher() {
+ @Override
+ public Object createService(ContextImpl ctx) {
+ return new DisplayManager(ctx.getOuterContext());
+ }});
registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
@@ -501,8 +514,13 @@
registerService(WINDOW_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
- return new WindowManagerImpl(ctx.getOuterContext(),
- Display.DEFAULT_DISPLAY);
+ Display display = ctx.mDisplay;
+ if (display == null) {
+ DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(
+ Context.DISPLAY_SERVICE);
+ display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ }
+ return new WindowManagerImpl(display);
}});
registerService(USER_SERVICE, new ServiceFetcher() {
@@ -993,7 +1011,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1006,7 +1024,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermission, false, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1020,7 +1038,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermission, true, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1053,7 +1071,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermission,
- true, false, Binder.getOrigCallingUser());
+ true, false, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1071,8 +1089,22 @@
}
@Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, receiverPermission, false, false,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- BroadcastReceiver resultReceiver, Handler scheduler,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
IIntentReceiver rd = null;
if (resultReceiver != null) {
@@ -1096,7 +1128,7 @@
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
- initialCode, initialData, initialExtras, null,
+ initialCode, initialData, initialExtras, receiverPermission,
true, false, user.getIdentifier());
} catch (RemoteException e) {
}
@@ -1110,7 +1142,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, true,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1143,7 +1175,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
- true, true, Binder.getOrigCallingUser());
+ true, true, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1158,7 +1190,67 @@
try {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().unbroadcastIntent(
- mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
+ mMainThread.getApplicationThread(), intent, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, null, false, true, user.getIdentifier());
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent,
+ UserHandle user, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ IIntentReceiver rd = null;
+ if (resultReceiver != null) {
+ if (mPackageInfo != null) {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = mPackageInfo.getReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler,
+ mMainThread.getInstrumentation(), false);
+ } else {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = new LoadedApk.ReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ }
+ }
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, rd,
+ initialCode, initialData, initialExtras, null,
+ true, true, user.getIdentifier());
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ if (resolvedType != null) {
+ intent = new Intent(intent);
+ intent.setDataAndType(intent.getData(), resolvedType);
+ }
+ try {
+ intent.setAllowFds(false);
+ ActivityManagerNative.getDefault().unbroadcastIntent(
+ mMainThread.getApplicationThread(), intent, user.getIdentifier());
} catch (RemoteException e) {
}
}
@@ -1171,11 +1263,18 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
- return registerReceiverInternal(receiver, filter, broadcastPermission,
- scheduler, getOuterContext());
+ return registerReceiverInternal(receiver, UserHandle.myUserId(),
+ filter, broadcastPermission, scheduler, getOuterContext());
}
- private Intent registerReceiverInternal(BroadcastReceiver receiver,
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return registerReceiverInternal(receiver, user.getIdentifier(),
+ filter, broadcastPermission, scheduler, getOuterContext());
+ }
+
+ private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
@@ -1198,7 +1297,7 @@
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
- rd, filter, broadcastPermission);
+ rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
@@ -1601,22 +1700,52 @@
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
+ if (overrideConfiguration == null) {
+ throw new IllegalArgumentException("overrideConfiguration must not be null");
+ }
+
ContextImpl c = new ContextImpl();
c.init(mPackageInfo, null, mMainThread);
c.mResources = mMainThread.getTopLevelResources(
- mPackageInfo.getResDir(), overrideConfiguration,
+ mPackageInfo.getResDir(),
+ getDisplayId(), overrideConfiguration,
mResources.getCompatibilityInfo());
return c;
}
@Override
+ public Context createDisplayContext(Display display) {
+ if (display == null) {
+ throw new IllegalArgumentException("display must not be null");
+ }
+
+ int displayId = display.getDisplayId();
+ CompatibilityInfo ci = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ CompatibilityInfoHolder cih = getCompatibilityInfo(displayId);
+ if (cih != null) {
+ ci = cih.get();
+ }
+
+ ContextImpl context = new ContextImpl();
+ context.init(mPackageInfo, null, mMainThread);
+ context.mDisplay = display;
+ context.mResources = mMainThread.getTopLevelResources(
+ mPackageInfo.getResDir(), displayId, null, ci);
+ return context;
+ }
+
+ private int getDisplayId() {
+ return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ }
+
+ @Override
public boolean isRestricted() {
return mRestricted;
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
- return mPackageInfo.mCompatibilityInfo;
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ return displayId == Display.DEFAULT_DISPLAY ? mPackageInfo.mCompatibilityInfo : null;
}
private File getDataDirFile() {
@@ -1660,6 +1789,7 @@
mResources = context.mResources;
mMainThread = context.mMainThread;
mContentResolver = context.mContentResolver;
+ mDisplay = context.mDisplay;
mOuterContext = this;
}
@@ -1683,7 +1813,8 @@
" compatiblity info:" + container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
- mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
+ mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,
+ null, container.getCompatibilityInfo());
}
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 16112cb..b3d99c5 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -147,15 +147,19 @@
this(context, theme, true);
}
- Dialog(Context context, int theme, boolean createContextWrapper) {
- if (theme == 0) {
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
- outValue, true);
- theme = outValue.resourceId;
+ Dialog(Context context, int theme, boolean createContextThemeWrapper) {
+ if (createContextThemeWrapper) {
+ if (theme == 0) {
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
+ outValue, true);
+ theme = outValue.resourceId;
+ }
+ mContext = new ContextThemeWrapper(context, theme);
+ } else {
+ mContext = context;
}
- mContext = createContextWrapper ? new ContextThemeWrapper(context, theme) : context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
@@ -164,7 +168,7 @@
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
-
+
/**
* @deprecated
* @hide
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 28876d3..3ff9df5 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -85,7 +85,7 @@
mSavedFragmentState = in.readBundle();
}
- public Fragment instantiate(Activity activity) {
+ public Fragment instantiate(Activity activity, Fragment parent) {
if (mInstance != null) {
return mInstance;
}
@@ -100,7 +100,7 @@
mSavedFragmentState.setClassLoader(activity.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
- mInstance.setIndex(mIndex);
+ mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
@@ -207,6 +207,8 @@
* with the fragment.
* <li> {@link #onActivityCreated} tells the fragment that its activity has
* completed its own {@link Activity#onCreate Activity.onCreate()}.
+ * <li> {@link #onViewStateRestored} tells the fragment that all of the saved
+ * state of its view hierarchy has been restored.
* <li> {@link #onStart} makes the fragment visible to the user (based on its
* containing activity being started).
* <li> {@link #onResume} makes the fragment interacting with the user (based on its
@@ -412,7 +414,13 @@
// Activity this fragment is attached to.
Activity mActivity;
-
+
+ // Private fragment manager for child fragments inside of this one.
+ FragmentManagerImpl mChildFragmentManager;
+
+ // If this Fragment is contained in another Fragment, this is that container.
+ Fragment mParentFragment;
+
// The optional identifier for this fragment -- either the container ID if it
// was dynamically added to the view hierarchy, or the ID supplied in
// layout.
@@ -595,16 +603,26 @@
}
}
- final void restoreViewState() {
+ final void restoreViewState(Bundle savedInstanceState) {
if (mSavedViewState != null) {
mView.restoreHierarchyState(mSavedViewState);
mSavedViewState = null;
}
+ mCalled = false;
+ onViewStateRestored(savedInstanceState);
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onViewStateRestored()");
+ }
}
- final void setIndex(int index) {
+ final void setIndex(int index, Fragment parent) {
mIndex = index;
- mWho = "android:fragment:" + mIndex;
+ if (parent != null) {
+ mWho = parent.mWho + ":" + mIndex;
+ } else {
+ mWho = "android:fragment:" + mIndex;
+ }
}
final boolean isInBackStack() {
@@ -785,12 +803,35 @@
* before {@link #getActivity()}, during the time from when the fragment is
* placed in a {@link FragmentTransaction} until it is committed and
* attached to its activity.
+ *
+ * <p>If this Fragment is a child of another Fragment, the FragmentManager
+ * returned here will be the parent's {@link #getChildFragmentManager()}.
*/
final public FragmentManager getFragmentManager() {
return mFragmentManager;
}
/**
+ * Return a private FragmentManager for placing and managing Fragments
+ * inside of this Fragment.
+ */
+ final public FragmentManager getChildFragmentManager() {
+ if (mChildFragmentManager == null) {
+ instantiateChildFragmentManager();
+ if (mState >= RESUMED) {
+ mChildFragmentManager.dispatchResume();
+ } else if (mState >= STARTED) {
+ mChildFragmentManager.dispatchStart();
+ } else if (mState >= ACTIVITY_CREATED) {
+ mChildFragmentManager.dispatchActivityCreated();
+ } else if (mState >= CREATED) {
+ mChildFragmentManager.dispatchCreate();
+ }
+ }
+ return mChildFragmentManager;
+ }
+
+ /**
* Return true if the fragment is currently added to its activity.
*/
final public boolean isAdded() {
@@ -880,6 +921,10 @@
* </ul>
*/
public void setRetainInstance(boolean retain) {
+ if (retain && mParentFragment != null) {
+ throw new IllegalStateException(
+ "Can't retain fragements that are nested in other fragments");
+ }
mRetainInstance = retain;
}
@@ -961,7 +1006,7 @@
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, true);
+ mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, true);
return mLoaderManager;
}
@@ -1191,7 +1236,7 @@
* {@link #setRetainInstance(boolean)} to retain their instance,
* as this callback tells the fragment when it is fully associated with
* the new activity instance. This is called after {@link #onCreateView}
- * and before {@link #onStart()}.
+ * and before {@link #onViewStateRestored(Bundle)}.
*
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
@@ -1199,7 +1244,22 @@
public void onActivityCreated(Bundle savedInstanceState) {
mCalled = true;
}
-
+
+ /**
+ * Called when all saved state has been restored into the view hierarchy
+ * of the fragment. This can be used to do initialization based on saved
+ * state that you are letting the view hierarchy track itself, such as
+ * whether check box widgets are currently checked. This is called
+ * after {@link #onActivityCreated(Bundle)} and before
+ * {@link #onStart()}.
+ *
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ */
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ mCalled = true;
+ }
+
/**
* Called when the Fragment is visible to the user. This is generally
* tied to {@link Activity#onStart() Activity.onStart} of the containing
@@ -1212,7 +1272,7 @@
mLoadersStarted = true;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+ mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doStart();
@@ -1305,7 +1365,7 @@
// + " mLoaderManager=" + mLoaderManager);
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+ mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
@@ -1530,6 +1590,14 @@
writer.print(prefix); writer.print("mActivity=");
writer.println(mActivity);
}
+ if (mChildFragmentManager != null) {
+ writer.print(prefix); writer.print("mChildFragmentManager=");
+ writer.println(mChildFragmentManager);
+ }
+ if (mParentFragment != null) {
+ writer.print(prefix); writer.print("mParentFragment=");
+ writer.println(mParentFragment);
+ }
if (mArguments != null) {
writer.print(prefix); writer.print("mArguments="); writer.println(mArguments);
}
@@ -1564,23 +1632,229 @@
writer.print(prefix); writer.println("Loader Manager:");
mLoaderManager.dump(prefix + " ", fd, writer, args);
}
+ if (mChildFragmentManager != null) {
+ writer.print(prefix); writer.println("Child Fragment Manager:");
+ mChildFragmentManager.dump(prefix + " ", fd, writer, args);
+ }
+ }
+
+ Fragment findFragmentByWho(String who) {
+ if (who.equals(mWho)) {
+ return this;
+ }
+ if (mChildFragmentManager != null) {
+ return mChildFragmentManager.findFragmentByWho(who);
+ }
+ return null;
+ }
+
+ void instantiateChildFragmentManager() {
+ mChildFragmentManager = new FragmentManagerImpl();
+ mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
+ @Override
+ public View findViewById(int id) {
+ if (mView == null) {
+ throw new IllegalStateException("Fragment does not have a view");
+ }
+ return mView.findViewById(id);
+ }
+ }, this);
+ }
+
+ void performCreate(Bundle savedInstanceState) {
+ mCalled = false;
+ onCreate(savedInstanceState);
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onCreate()");
+ }
+ if (savedInstanceState != null) {
+ Parcelable p = savedInstanceState.getParcelable(Activity.FRAGMENTS_TAG);
+ if (p != null) {
+ if (mChildFragmentManager == null) {
+ instantiateChildFragmentManager();
+ }
+ mChildFragmentManager.restoreAllState(p, null);
+ mChildFragmentManager.dispatchCreate();
+ }
+ }
+ }
+
+ void performActivityCreated(Bundle savedInstanceState) {
+ mCalled = false;
+ onActivityCreated(savedInstanceState);
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onActivityCreated()");
+ }
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchActivityCreated();
+ }
}
void performStart() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.noteStateNotSaved();
+ mChildFragmentManager.execPendingActions();
+ }
+ mCalled = false;
onStart();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onStart()");
+ }
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchStart();
+ }
if (mLoaderManager != null) {
mLoaderManager.doReportStart();
}
}
+ void performResume() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.execPendingActions();
+ }
+ mCalled = false;
+ onResume();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onResume()");
+ }
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchResume();
+ mChildFragmentManager.execPendingActions();
+ }
+ }
+
+ void performConfigurationChanged(Configuration newConfig) {
+ onConfigurationChanged(newConfig);
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchConfigurationChanged(newConfig);
+ }
+ }
+
+ void performLowMemory() {
+ onLowMemory();
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchLowMemory();
+ }
+ }
+
+ void performTrimMemory(int level) {
+ onTrimMemory(level);
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchTrimMemory(level);
+ }
+ }
+
+ boolean performCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ boolean show = false;
+ if (!mHidden) {
+ if (mHasMenu && mMenuVisible) {
+ show = true;
+ onCreateOptionsMenu(menu, inflater);
+ }
+ if (mChildFragmentManager != null) {
+ show |= mChildFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
+ }
+ }
+ return show;
+ }
+
+ boolean performPrepareOptionsMenu(Menu menu) {
+ boolean show = false;
+ if (!mHidden) {
+ if (mHasMenu && mMenuVisible) {
+ show = true;
+ onPrepareOptionsMenu(menu);
+ }
+ if (mChildFragmentManager != null) {
+ show |= mChildFragmentManager.dispatchPrepareOptionsMenu(menu);
+ }
+ }
+ return show;
+ }
+
+ boolean performOptionsItemSelected(MenuItem item) {
+ if (!mHidden) {
+ if (mHasMenu && mMenuVisible) {
+ if (onOptionsItemSelected(item)) {
+ return true;
+ }
+ }
+ if (mChildFragmentManager != null) {
+ if (mChildFragmentManager.dispatchOptionsItemSelected(item)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean performContextItemSelected(MenuItem item) {
+ if (!mHidden) {
+ if (onContextItemSelected(item)) {
+ return true;
+ }
+ if (mChildFragmentManager != null) {
+ if (mChildFragmentManager.dispatchContextItemSelected(item)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void performOptionsMenuClosed(Menu menu) {
+ if (!mHidden) {
+ if (mHasMenu && mMenuVisible) {
+ onOptionsMenuClosed(menu);
+ }
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchOptionsMenuClosed(menu);
+ }
+ }
+ }
+
+ void performSaveInstanceState(Bundle outState) {
+ onSaveInstanceState(outState);
+ if (mChildFragmentManager != null) {
+ Parcelable p = mChildFragmentManager.saveAllState();
+ if (p != null) {
+ outState.putParcelable(Activity.FRAGMENTS_TAG, p);
+ }
+ }
+ }
+
+ void performPause() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchPause();
+ }
+ mCalled = false;
+ onPause();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onPause()");
+ }
+ }
+
void performStop() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchStop();
+ }
+ mCalled = false;
onStop();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onStop()");
+ }
if (mLoadersStarted) {
mLoadersStarted = false;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+ mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
if (mActivity == null || !mActivity.mChangingConfigurations) {
@@ -1593,9 +1867,29 @@
}
void performDestroyView() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchDestroyView();
+ }
+ mCalled = false;
onDestroyView();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onDestroyView()");
+ }
if (mLoaderManager != null) {
mLoaderManager.doReportNextStart();
}
}
+
+ void performDestroy() {
+ if (mChildFragmentManager != null) {
+ mChildFragmentManager.dispatchDestroy();
+ }
+ mCalled = false;
+ onDestroy();
+ if (!mCalled) {
+ throw new SuperNotCalledException("Fragment " + this
+ + " did not call through to super.onDestroy()");
+ }
+ }
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 52a6557..eaaf0d7 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -20,7 +20,6 @@
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
@@ -30,7 +29,6 @@
import android.util.DebugUtils;
import android.util.Log;
import android.util.LogWriter;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
@@ -381,6 +379,13 @@
}
/**
+ * Callbacks from FragmentManagerImpl to its container.
+ */
+interface FragmentContainer {
+ public View findViewById(int id);
+}
+
+/**
* Container for fragments associated with an activity.
*/
final class FragmentManagerImpl extends FragmentManager {
@@ -410,6 +415,8 @@
int mCurState = Fragment.INITIALIZING;
Activity mActivity;
+ FragmentContainer mContainer;
+ Fragment mParent;
boolean mNeedMenuInvalidate;
boolean mStateSaved;
@@ -585,7 +592,11 @@
sb.append("FragmentManager{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" in ");
- DebugUtils.buildShortClassTag(mActivity, sb);
+ if (mParent != null) {
+ DebugUtils.buildShortClassTag(mParent, sb);
+ } else {
+ DebugUtils.buildShortClassTag(mActivity, sb);
+ }
sb.append("}}");
return sb.toString();
}
@@ -681,6 +692,11 @@
}
writer.print(prefix); writer.println("FragmentManager misc state:");
+ writer.print(prefix); writer.print(" mActivity="); writer.println(mActivity);
+ writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer);
+ if (mParent != null) {
+ writer.print(prefix); writer.print(" mParent="); writer.println(mParent);
+ }
writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState);
writer.print(" mStateSaved="); writer.print(mStateSaved);
writer.print(" mDestroyed="); writer.println(mDestroyed);
@@ -809,7 +825,9 @@
}
}
f.mActivity = mActivity;
- f.mFragmentManager = mActivity.mFragments;
+ f.mParentFragment = mParent;
+ f.mFragmentManager = mParent != null
+ ? mParent.mChildFragmentManager : mActivity.mFragments;
f.mCalled = false;
f.onAttach(mActivity);
if (!f.mCalled) {
@@ -817,14 +835,9 @@
+ " did not call through to super.onAttach()");
}
mActivity.onAttachFragment(f);
-
+
if (!f.mRetaining) {
- f.mCalled = false;
- f.onCreate(f.mSavedFragmentState);
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onCreate()");
- }
+ f.performCreate(f.mSavedFragmentState);
}
f.mRetaining = false;
if (f.mFromLayout) {
@@ -845,7 +858,7 @@
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
- container = (ViewGroup)mActivity.findViewById(f.mContainerId);
+ container = (ViewGroup)mContainer.findViewById(f.mContainerId);
if (container == null && !f.mRestored) {
throwException(new IllegalArgumentException(
"No view found for id 0x"
@@ -873,14 +886,9 @@
}
}
- f.mCalled = false;
- f.onActivityCreated(f.mSavedFragmentState);
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onActivityCreated()");
- }
+ f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {
- f.restoreViewState();
+ f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
@@ -888,23 +896,13 @@
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
- f.mCalled = false;
f.performStart();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onStart()");
- }
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
- f.mCalled = false;
f.mResumed = true;
- f.onResume();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onResume()");
- }
+ f.performResume();
// Get rid of this in case we saved it and never needed it.
f.mSavedFragmentState = null;
f.mSavedViewState = null;
@@ -915,23 +913,13 @@
case Fragment.RESUMED:
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
- f.mCalled = false;
- f.onPause();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onPause()");
- }
+ f.performPause();
f.mResumed = false;
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
- f.mCalled = false;
f.performStop();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onStop()");
- }
}
case Fragment.STOPPED:
case Fragment.ACTIVITY_CREATED:
@@ -944,12 +932,7 @@
saveFragmentViewState(f);
}
}
- f.mCalled = false;
f.performDestroyView();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onDestroyView()");
- }
if (f.mView != null && f.mContainer != null) {
Animator anim = null;
if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
@@ -1008,12 +991,7 @@
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
- f.mCalled = false;
- f.onDestroy();
- if (!f.mCalled) {
- throw new SuperNotCalledException("Fragment " + f
- + " did not call through to super.onDestroy()");
- }
+ f.performDestroy();
}
f.mCalled = false;
@@ -1027,6 +1005,7 @@
makeInactive(f);
} else {
f.mActivity = null;
+ f.mParentFragment = null;
f.mFragmentManager = null;
}
}
@@ -1050,11 +1029,11 @@
if (mActivity == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
-
+
if (!always && mCurState == newState) {
return;
}
-
+
mCurState = newState;
if (mActive != null) {
boolean loadersRunning = false;
@@ -1099,11 +1078,11 @@
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
- f.setIndex(mActive.size());
+ f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
- f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
+ f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
@@ -1120,7 +1099,7 @@
mAvailIndices = new ArrayList<Integer>();
}
mAvailIndices.add(f.mIndex);
- mActivity.invalidateFragmentIndex(f.mIndex);
+ mActivity.invalidateFragment(f.mWho);
f.initState();
}
@@ -1296,7 +1275,7 @@
if (mActive != null && who != null) {
for (int i=mActive.size()-1; i>=0; i--) {
Fragment f = mActive.get(i);
- if (f != null && who.equals(f.mWho)) {
+ if (f != null && (f=f.findFragmentByWho(who)) != null) {
return f;
}
}
@@ -1566,7 +1545,7 @@
if (mStateBundle == null) {
mStateBundle = new Bundle();
}
- f.onSaveInstanceState(mStateBundle);
+ f.performSaveInstanceState(mStateBundle);
if (!mStateBundle.isEmpty()) {
result = mStateBundle;
mStateBundle = null;
@@ -1735,7 +1714,7 @@
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
- Fragment f = fs.instantiate(mActivity);
+ Fragment f = fs.instantiate(mActivity, mParent);
if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
@@ -1803,9 +1782,11 @@
}
}
- public void attachActivity(Activity activity) {
+ public void attachActivity(Activity activity, FragmentContainer container, Fragment parent) {
if (mActivity != null) throw new IllegalStateException("Already attached");
mActivity = activity;
+ mContainer = container;
+ mParent = parent;
}
public void noteStateNotSaved() {
@@ -1840,11 +1821,17 @@
moveToState(Fragment.STOPPED, false);
}
+ public void dispatchDestroyView() {
+ moveToState(Fragment.CREATED, false);
+ }
+
public void dispatchDestroy() {
mDestroyed = true;
execPendingActions();
moveToState(Fragment.INITIALIZING, false);
mActivity = null;
+ mContainer = null;
+ mParent = null;
}
public void dispatchConfigurationChanged(Configuration newConfig) {
@@ -1852,7 +1839,7 @@
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null) {
- f.onConfigurationChanged(newConfig);
+ f.performConfigurationChanged(newConfig);
}
}
}
@@ -1863,7 +1850,7 @@
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null) {
- f.onLowMemory();
+ f.performLowMemory();
}
}
}
@@ -1874,7 +1861,7 @@
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null) {
- f.onTrimMemory(level);
+ f.performTrimMemory(level);
}
}
}
@@ -1886,13 +1873,14 @@
if (mAdded != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
- if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
- show = true;
- f.onCreateOptionsMenu(menu, inflater);
- if (newMenus == null) {
- newMenus = new ArrayList<Fragment>();
+ if (f != null) {
+ if (f.performCreateOptionsMenu(menu, inflater)) {
+ show = true;
+ if (newMenus == null) {
+ newMenus = new ArrayList<Fragment>();
+ }
+ newMenus.add(f);
}
- newMenus.add(f);
}
}
}
@@ -1916,9 +1904,10 @@
if (mAdded != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
- if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
- show = true;
- f.onPrepareOptionsMenu(menu);
+ if (f != null) {
+ if (f.performPrepareOptionsMenu(menu)) {
+ show = true;
+ }
}
}
}
@@ -1929,8 +1918,8 @@
if (mAdded != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
- if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
- if (f.onOptionsItemSelected(item)) {
+ if (f != null) {
+ if (f.performOptionsItemSelected(item)) {
return true;
}
}
@@ -1943,8 +1932,8 @@
if (mAdded != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
- if (f != null && !f.mHidden) {
- if (f.onContextItemSelected(item)) {
+ if (f != null) {
+ if (f.performContextItemSelected(item)) {
return true;
}
}
@@ -1957,8 +1946,8 @@
if (mAdded != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
- if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) {
- f.onOptionsMenuClosed(menu);
+ if (f != null) {
+ f.performOptionsMenuClosed(menu);
}
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c3e911e..7a633ed 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -66,7 +66,7 @@
public int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int startFlags, Configuration newConfig,
- Bundle options) throws RemoteException;
+ Bundle options, int userId) throws RemoteException;
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -80,7 +80,7 @@
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
- String requiredPermission) throws RemoteException;
+ String requiredPermission, int userId) throws RemoteException;
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
@@ -177,13 +177,16 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options) throws RemoteException;
+ int flags, Bundle options, int userId) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public int getUidForIntentSender(IIntentSender sender) throws RemoteException;
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) throws RemoteException;
+
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
@@ -272,11 +275,6 @@
public void stopAppSwitches() throws RemoteException;
public void resumeAppSwitches() throws RemoteException;
- public int startActivityInPackage(int uid,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options)
- throws RemoteException;
-
public void killApplicationWithUid(String pkg, int uid) throws RemoteException;
public void closeSystemDialogs(String reason) throws RemoteException;
@@ -316,9 +314,6 @@
public int startActivities(IApplicationThread caller,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
Bundle options) throws RemoteException;
- public int startActivitiesInPackage(int uid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) throws RemoteException;
public int getFrontActivityScreenCompatMode() throws RemoteException;
public void setFrontActivityScreenCompatMode(int mode) throws RemoteException;
@@ -331,6 +326,7 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
+ public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
@@ -550,9 +546,8 @@
int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
+ int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
-
- int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
@@ -579,7 +574,7 @@
int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
- int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
+
int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
int GET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
int SET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
@@ -611,4 +606,5 @@
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151;
int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152;
+ int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index f60cfd6..03a26d4 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
- int resultCode, String data, Bundle extras, boolean sync) throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean sync,
+ int sendingUser) throws RemoteException;
static final int BACKUP_MODE_INCREMENTAL = 0;
static final int BACKUP_MODE_FULL = 1;
static final int BACKUP_MODE_RESTORE = 2;
@@ -105,8 +106,8 @@
void dumpProvider(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String data, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 6f95e26..62d4962 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -24,16 +24,13 @@
/** {@hide} */
interface INotificationManager
{
- /** @deprecated use {@link #enqueueNotificationWithTag} instead */
- void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
- /** @deprecated use {@link #cancelNotificationWithTag} instead */
- void cancelNotification(String pkg, int id);
- void cancelAllNotifications(String pkg);
+ void cancelAllNotifications(String pkg, int userId);
void enqueueToast(String pkg, ITransientNotification callback, int duration);
void cancelToast(String pkg, ITransientNotification callback);
- void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
- void cancelNotificationWithTag(String pkg, String tag, int id);
+ void enqueueNotificationWithTag(String pkg, String tag, int id,
+ in Notification notification, inout int[] idReceived, int userId);
+ void cancelNotificationWithTag(String pkg, String tag, int id, int userId);
void setNotificationsEnabledForPackage(String pkg, boolean enabled);
boolean areNotificationsEnabledForPackage(String pkg);
diff --git a/core/java/android/app/IStopUserCallback.aidl b/core/java/android/app/IStopUserCallback.aidl
new file mode 100644
index 0000000..19ac1d5
--- /dev/null
+++ b/core/java/android/app/IStopUserCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** 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.
+*/
+
+package android.app;
+
+/**
+ * Callback to find out when we have finished stopping a user.
+ * {@hide}
+ */
+interface IStopUserCallback
+{
+ void userStopped(int userId);
+ void userStopAborted(int userId);
+}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e89bb2..0a9ed58 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -41,6 +41,7 @@
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.IOException;
@@ -139,7 +140,8 @@
ContextImpl.createSystemContext(mainThread);
ActivityThread.mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
- mainThread.getDisplayMetricsLocked(compatInfo, false),
+ mainThread.getDisplayMetricsLocked(
+ Display.DEFAULT_DISPLAY, compatInfo),
compatInfo);
//Slog.i(TAG, "Created system resources "
// + mSystemContext.getResources() + ": "
@@ -471,7 +473,8 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir, null, this);
+ mResources = mainThread.getTopLevelResources(mResDir,
+ Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
}
@@ -667,8 +670,8 @@
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
@@ -677,7 +680,7 @@
}
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
- ordered, sticky);
+ ordered, sticky, sendingUser);
} else {
// The activity manager dispatched a broadcast to a registered
// receiver in this process, but before it could be delivered the
@@ -713,10 +716,10 @@
private final boolean mOrdered;
public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky) {
+ boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
- ordered, sticky, mIIntentReceiver.asBinder());
+ ordered, sticky, mIIntentReceiver.asBinder(), sendingUser);
mCurIntent = intent;
mOrdered = ordered;
}
@@ -827,14 +830,15 @@
return mUnregisterLocation;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
- Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
+ Args args = new Args(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index ff71ee7..fd0f0bf 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -17,7 +17,6 @@
package android.app;
import android.content.Loader;
-import android.content.Loader.OnLoadCanceledListener;
import android.os.Bundle;
import android.util.DebugUtils;
import android.util.Log;
@@ -213,6 +212,8 @@
// previously run loader until the new loader's data is available.
final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
+ final String mWho;
+
Activity mActivity;
boolean mStarted;
boolean mRetaining;
@@ -529,7 +530,8 @@
}
}
- LoaderManagerImpl(Activity activity, boolean started) {
+ LoaderManagerImpl(String who, Activity activity, boolean started) {
+ mWho = who;
mActivity = activity;
mStarted = started;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 69c20b0..c095280 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
/**
@@ -125,7 +126,27 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
- service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
+ service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
+ UserHandle.myUserId());
+ if (id != idOut[0]) {
+ Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
+ {
+ int[] idOut = new int[1];
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+ try {
+ service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
+ user.getIdentifier());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -154,7 +175,21 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
try {
- service.cancelNotificationWithTag(pkg, tag, id);
+ service.cancelNotificationWithTag(pkg, tag, id, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void cancelAsUser(String tag, int id, UserHandle user)
+ {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
+ try {
+ service.cancelNotificationWithTag(pkg, tag, id, user.getIdentifier());
} catch (RemoteException e) {
}
}
@@ -169,7 +204,7 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
- service.cancelAllNotifications(pkg);
+ service.cancelAllNotifications(pkg, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index a57c516..a3c1838 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -147,8 +147,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
@@ -228,7 +228,29 @@
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, options);
+ flags, options, UserHandle.myUserId());
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public static PendingIntent getActivityAsUser(Context context, int requestCode,
+ Intent intent, int flags, Bundle options, UserHandle user) {
+ String packageName = context.getPackageName();
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ context.getContentResolver()) : null;
+ try {
+ intent.setAllowFds(false);
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null,
+ flags, options, user.getIdentifier());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -334,7 +356,8 @@
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- null, null, requestCode, intents, resolvedTypes, flags, options);
+ null, null, requestCode, intents, resolvedTypes, flags, options,
+ UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -372,7 +395,7 @@
ActivityManager.INTENT_SENDER_BROADCAST, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, null);
+ flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -411,7 +434,7 @@
ActivityManager.INTENT_SENDER_SERVICE, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, null);
+ flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
new file mode 100644
index 0000000..eb5a652
--- /dev/null
+++ b/core/java/android/app/Presentation.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.WindowManagerImpl;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+
+/**
+ * Base class for presentations.
+ *
+ * A presentation is a special kind of dialog whose purpose is to present
+ * content on a secondary display. A {@link Presentation} is associated with
+ * the target {@link Display} at creation time and configures its context and
+ * resource configuration according to the display's metrics.
+ *
+ * Notably, the {@link Context} of a presentation is different from the context
+ * of its containing {@link Activity}. It is important to inflate the layout
+ * of a presentation and load other resources using the presentation's own context
+ * to ensure that assets of the correct size and density for the target display
+ * are loaded.
+ *
+ * A presentation is automatically canceled (see {@link Dialog#cancel()}) when
+ * the display to which it is attached is removed. An activity should take
+ * care of pausing and resuming whatever content is playing within the presentation
+ * whenever the activity itself is paused or resume.
+ *
+ * @see {@link DisplayManager} for information on how to enumerate displays.
+ */
+public class Presentation extends Dialog {
+ private static final String TAG = "Presentation";
+
+ private static final int MSG_CANCEL = 1;
+
+ private final Display mDisplay;
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * Creates a new presentation that is attached to the specified display
+ * using the default theme.
+ *
+ * @param outerContext The context of the application that is showing the presentation.
+ * The presentation will create its own context (see {@link #getContext()}) based
+ * on this context and information about the associated display.
+ * @param display The display to which the presentation should be attached.
+ */
+ public Presentation(Context outerContext, Display display) {
+ this(outerContext, display, 0);
+ }
+
+ /**
+ * Creates a new presentation that is attached to the specified display
+ * using the optionally specified theme.
+ *
+ * @param outerContext The context of the application that is showing the presentation.
+ * The presentation will create its own context (see {@link #getContext()}) based
+ * on this context and information about the associated display.
+ * @param display The display to which the presentation should be attached.
+ * @param theme A style resource describing the theme to use for the window.
+ * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
+ * Style and Theme Resources</a> for more information about defining and using
+ * styles. This theme is applied on top of the current theme in
+ * <var>outerContext</var>. If 0, the default presentation theme will be used.
+ */
+ public Presentation(Context outerContext, Display display, int theme) {
+ super(createPresentationContext(outerContext, display, theme), theme, false);
+
+ mDisplay = display;
+ mDisplayManager = (DisplayManager)getContext().getSystemService(Context.DISPLAY_SERVICE);
+
+ getWindow().setGravity(Gravity.FILL);
+ setCanceledOnTouchOutside(false);
+ }
+
+ /**
+ * Gets the {@link Display} that this presentation appears on.
+ *
+ * @return The display.
+ */
+ public Display getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Gets the {@link Resources} that should be used to inflate the layout of this presentation.
+ * This resources object has been configured according to the metrics of the
+ * display that the presentation appears on.
+ *
+ * @return The presentation resources object.
+ */
+ public Resources getResources() {
+ return getContext().getResources();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+
+ // Since we were not watching for display changes until just now, there is a
+ // chance that the display metrics have changed. If so, we will need to
+ // dismiss the presentation immediately. This case is expected
+ // to be rare but surprising, so we'll write a log message about it.
+ if (!isConfigurationStillValid()) {
+ Log.i(TAG, "Presentation is being immediately dismissed because the "
+ + "display metrics have changed since it was created.");
+ mHandler.sendEmptyMessage(MSG_CANCEL);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ super.onStop();
+ }
+
+ /**
+ * Called by the system when the {@link Display} to which the presentation
+ * is attached has been removed.
+ *
+ * The system automatically calls {@link #cancel} to dismiss the presentation
+ * after sending this event.
+ *
+ * @see #getDisplay
+ */
+ public void onDisplayRemoved() {
+ }
+
+ /**
+ * Called by the system when the properties of the {@link Display} to which
+ * the presentation is attached have changed.
+ *
+ * If the display metrics have changed (for example, if the display has been
+ * resized or rotated), then the system automatically calls
+ * {@link #cancel} to dismiss the presentation.
+ *
+ * @see #getDisplay
+ */
+ public void onDisplayChanged() {
+ }
+
+ private void handleDisplayRemoved() {
+ onDisplayRemoved();
+ cancel();
+ }
+
+ private void handleDisplayChanged() {
+ onDisplayChanged();
+
+ // We currently do not support configuration changes for presentations
+ // (although we could add that feature with a bit more work).
+ // If the display metrics have changed in any way then the current configuration
+ // is invalid and the application must recreate the presentation to get
+ // a new context.
+ if (!isConfigurationStillValid()) {
+ cancel();
+ }
+ }
+
+ private boolean isConfigurationStillValid() {
+ DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+ return dm.equals(getResources().getDisplayMetrics());
+ }
+
+ private static Context createPresentationContext(
+ Context outerContext, Display display, int theme) {
+ if (outerContext == null) {
+ throw new IllegalArgumentException("outerContext must not be null");
+ }
+ if (display == null) {
+ throw new IllegalArgumentException("display must not be null");
+ }
+
+ Context displayContext = outerContext.createDisplayContext(display);
+ if (theme == 0) {
+ TypedValue outValue = new TypedValue();
+ displayContext.getTheme().resolveAttribute(
+ com.android.internal.R.attr.presentationTheme, outValue, true);
+ theme = outValue.resourceId;
+ }
+
+ // Derive the display's window manager from the outer window manager.
+ // We do this because the outer window manager have some extra information
+ // such as the parent window, which is important if the presentation uses
+ // an application window type.
+ final WindowManagerImpl outerWindowManager =
+ (WindowManagerImpl)outerContext.getSystemService(Context.WINDOW_SERVICE);
+ final WindowManagerImpl displayWindowManager =
+ outerWindowManager.createPresentationWindowManager(display);
+ return new ContextThemeWrapper(displayContext, theme) {
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.WINDOW_SERVICE.equals(name)) {
+ return displayWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+ };
+ }
+
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDisplay.getDisplayId()) {
+ handleDisplayRemoved();
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDisplay.getDisplayId()) {
+ handleDisplayChanged();
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CANCEL:
+ cancel();
+ break;
+ }
+ }
+ };
+}
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 1c37414..c8062ca 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -50,23 +50,23 @@
*/
public final class DeviceAdminInfo implements Parcelable {
static final String TAG = "DeviceAdminInfo";
-
+
/**
* A type of policy that this device admin can use: limit the passwords
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
- *
+ *
* <p>To control this policy, the device admin must have a "limit-password"
* tag in the "uses-policies" section of its meta-data.
*/
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
-
+
/**
* A type of policy that this device admin can use: able to watch login
* attempts from the user, via {@link DeviceAdminReceiver#ACTION_PASSWORD_FAILED},
* {@link DeviceAdminReceiver#ACTION_PASSWORD_SUCCEEDED}, and
* {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}.
- *
+ *
* <p>To control this policy, the device admin must have a "watch-login"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -76,7 +76,7 @@
* A type of policy that this device admin can use: able to reset the
* user's password via
* {@link DevicePolicyManager#resetPassword}.
- *
+ *
* <p>To control this policy, the device admin must have a "reset-password"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -87,7 +87,7 @@
* to lock via{@link DevicePolicyManager#lockNow} or limit the
* maximum lock timeout for the device via
* {@link DevicePolicyManager#setMaximumTimeToLock}.
- *
+ *
* <p>To control this policy, the device admin must have a "force-lock"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -97,7 +97,7 @@
* A type of policy that this device admin can use: able to factory
* reset the device, erasing all of the user's data, via
* {@link DevicePolicyManager#wipeData}.
- *
+ *
* <p>To control this policy, the device admin must have a "wipe-data"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -138,13 +138,21 @@
*/
public static final int USES_POLICY_DISABLE_CAMERA = 8;
+ /**
+ * A type of policy that this device admin can use: disables use of keyguard widgets.
+ *
+ * <p>To control this policy, the device admin must have a "disable-keyguard-widgets"
+ * tag in the "uses-policies" section of its meta-data.
+ */
+ public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9;
+
/** @hide */
public static class PolicyInfo {
public final int ident;
final public String tag;
final public int label;
final public int description;
-
+
public PolicyInfo(int identIn, String tagIn, int labelIn, int descriptionIn) {
ident = identIn;
tag = tagIn;
@@ -152,11 +160,11 @@
description = descriptionIn;
}
}
-
+
static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>();
static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>();
static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>();
-
+
static {
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data",
com.android.internal.R.string.policylab_wipeData,
@@ -185,6 +193,10 @@
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera",
com.android.internal.R.string.policylab_disableCamera,
com.android.internal.R.string.policydesc_disableCamera));
+ sPoliciesDisplayOrder.add(new PolicyInfo(
+ USES_POLICY_DISABLE_KEYGUARD_WIDGETS, "disable-keyguard-widgets",
+ com.android.internal.R.string.policylab_disableKeyguardWidgets,
+ com.android.internal.R.string.policydesc_disableKeyguardWidgets));
for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
PolicyInfo pi = sPoliciesDisplayOrder.get(i);
@@ -192,25 +204,25 @@
sKnownPolicies.put(pi.tag, pi.ident);
}
}
-
+
/**
* The BroadcastReceiver that implements this device admin component.
*/
final ResolveInfo mReceiver;
-
+
/**
* Whether this should be visible to the user.
*/
boolean mVisible;
-
+
/**
* The policies this administrator needs access to.
*/
int mUsesPolicies;
-
+
/**
* Constructor.
- *
+ *
* @param context The Context in which we are parsing the device admin.
* @param receiver The ResolveInfo returned from the package manager about
* this device admin's component.
@@ -219,9 +231,9 @@
throws XmlPullParserException, IOException {
mReceiver = receiver;
ActivityInfo ai = receiver.activityInfo;
-
+
PackageManager pm = context.getPackageManager();
-
+
XmlResourceParser parser = null;
try {
parser = ai.loadXmlMetaData(pm, DeviceAdminReceiver.DEVICE_ADMIN_META_DATA);
@@ -229,30 +241,30 @@
throw new XmlPullParserException("No "
+ DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data");
}
-
+
Resources res = pm.getResourcesForApplication(ai.applicationInfo);
-
+
AttributeSet attrs = Xml.asAttributeSet(parser);
-
+
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
-
+
String nodeName = parser.getName();
if (!"device-admin".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with device-admin tag");
}
-
+
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.DeviceAdmin);
mVisible = sa.getBoolean(
com.android.internal.R.styleable.DeviceAdmin_visible, true);
-
+
sa.recycle();
-
+
int outerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -290,14 +302,14 @@
mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
mUsesPolicies = source.readInt();
}
-
+
/**
* Return the .apk package that implements this device admin.
*/
public String getPackageName() {
return mReceiver.activityInfo.packageName;
}
-
+
/**
* Return the class name of the receiver component that implements
* this device admin.
@@ -321,20 +333,20 @@
return new ComponentName(mReceiver.activityInfo.packageName,
mReceiver.activityInfo.name);
}
-
+
/**
* Load the user-displayed label for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public CharSequence loadLabel(PackageManager pm) {
return mReceiver.loadLabel(pm);
}
-
+
/**
* Load user-visible description associated with this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
@@ -351,17 +363,17 @@
}
throw new NotFoundException();
}
-
+
/**
* Load the user-displayed icon for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public Drawable loadIcon(PackageManager pm) {
return mReceiver.loadIcon(pm);
}
-
+
/**
* Returns whether this device admin would like to be visible to the
* user, even when it is not enabled.
@@ -369,7 +381,7 @@
public boolean isVisible() {
return mVisible;
}
-
+
/**
* Return true if the device admin has requested that it be able to use
* the given policy control. The possible policy identifier inputs are:
@@ -382,7 +394,7 @@
public boolean usesPolicy(int policyIdent) {
return (mUsesPolicies & (1<<policyIdent)) != 0;
}
-
+
/**
* Return the XML tag name for the given policy identifier. Valid identifiers
* are as per {@link #usesPolicy(int)}. If the given identifier is not
@@ -391,7 +403,7 @@
public String getTagForPolicy(int policyIdent) {
return sRevKnownPolicies.get(policyIdent).tag;
}
-
+
/** @hide */
public ArrayList<PolicyInfo> getUsedPolicies() {
ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
@@ -403,25 +415,25 @@
}
return res;
}
-
+
/** @hide */
public void writePoliciesToXml(XmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
out.attribute(null, "flags", Integer.toString(mUsesPolicies));
}
-
+
/** @hide */
public void readPoliciesFromXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
mUsesPolicies = Integer.parseInt(
parser.getAttributeValue(null, "flags"));
}
-
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Receiver:");
mReceiver.dump(pw, prefix + " ");
}
-
+
@Override
public String toString() {
return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
@@ -429,7 +441,7 @@
/**
* Used to package this object into a {@link Parcel}.
- *
+ *
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0b58396..4c55bb3 100755
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1155,6 +1155,16 @@
= "android.app.action.START_ENCRYPTION";
/**
+ * Widgets are enabled in keyguard
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0;
+
+ /**
+ * Disable all keyguard widgets
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 0x7fffffff;
+
+ /**
* Called by an application that is administering the device to
* request that the storage system be encrypted.
*
@@ -1284,6 +1294,46 @@
}
/**
+ * Called by an application that is administering the device to disable adding widgets to
+ * keyguard. After setting this, keyguard widgets will be disabled according to the state
+ * provided.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_WIDGETS} to be able to call
+ * this method; if it has not, a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param which {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_ALL} or
+ * {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_NONE} (the default).
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName admin, int which) {
+ if (mService != null) {
+ try {
+ mService.setKeyguardWidgetsDisabled(admin, which);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Determine whether or not widgets have been disabled in keyguard either by the current
+ * admin, if specified, or all admins.
+ * @param admin The name of the admin component to check, or null to check if any admins
+ * have disabled widgets in keyguard.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getKeyguardWidgetsDisabled(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return KEYGUARD_DISABLE_WIDGETS_NONE;
+ }
+
+ /**
* @hide
*/
public void setActiveAdmin(ComponentName policyReceiver, boolean refreshing) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9419a62..0b7ec12 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -48,7 +48,7 @@
void setPasswordMinimumNonLetter(in ComponentName who, int length);
int getPasswordMinimumNonLetter(in ComponentName who);
-
+
void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who);
@@ -59,17 +59,17 @@
boolean isActivePasswordSufficient();
int getCurrentFailedPasswordAttempts();
-
+
void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
int getMaximumFailedPasswordsForWipe(in ComponentName admin);
-
+
boolean resetPassword(String password, int flags);
-
+
void setMaximumTimeToLock(in ComponentName who, long timeMs);
long getMaximumTimeToLock(in ComponentName who);
-
+
void lockNow();
-
+
void wipeData(int flags);
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
@@ -82,6 +82,9 @@
void setCameraDisabled(in ComponentName who, boolean disabled);
boolean getCameraDisabled(in ComponentName who);
+ void setKeyguardWidgetsDisabled(in ComponentName who, int which);
+ int getKeyguardWidgetsDisabled(in ComponentName who);
+
void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing);
boolean isAdminActive(in ComponentName policyReceiver);
List<ComponentName> getActiveAdmins();
@@ -89,7 +92,7 @@
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy);
-
+
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
int numbers, int symbols, int nonletter);
void reportFailedPasswordAttempt();
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index a74a268..9e8ab2c 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Point;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
import android.view.WindowManager;
@@ -39,8 +41,12 @@
// This path must match what the WallpaperManagerService uses
// TODO: Will need to change if backing up non-primary user's wallpaper
- public static final String WALLPAPER_IMAGE = "/data/system/users/0/wallpaper";
- public static final String WALLPAPER_INFO = "/data/system/users/0/wallpaper_info.xml";
+ public static final String WALLPAPER_IMAGE =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper").getAbsolutePath();
+ public static final String WALLPAPER_INFO =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper_info.xml").getAbsolutePath();
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
public static final String WALLPAPER_IMAGE_KEY =
"/data/data/com.android.settings/files/wallpaper";
@@ -50,7 +56,9 @@
// will be saved to this file from the restore stream, then renamed to the proper
// location if it's deemed suitable.
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String STAGE_FILE = "/data/system/users/0/wallpaper-tmp";
+ private static final String STAGE_FILE =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper-tmp").getAbsolutePath();
Context mContext;
String[] mFiles;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 17d404d..f817fb4 100755
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -75,6 +75,7 @@
public final class BluetoothAdapter {
private static final String TAG = "BluetoothAdapter";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Sentinel error value for this class. Guaranteed to not equal any other
@@ -465,7 +466,7 @@
if (mService != null)
{
int state= mService.getState();
- if (DBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+ if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
return state;
}
// TODO(BT) there might be a small gap during STATE_TURNING_ON that
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index b2b5d81..30406e9 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -133,6 +133,11 @@
return true;
}
+ @Override
+ public void captivePortalCheckComplete() {
+ // not implemented
+ }
+
/**
* Re-enable connectivity to a network after a {@link #teardown()}.
*/
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 446f1af..1500b00 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -237,16 +237,17 @@
final boolean mOrderedHint;
final boolean mInitialStickyHint;
final IBinder mToken;
+ final int mSendingUser;
int mResultCode;
String mResultData;
Bundle mResultExtras;
boolean mAbortBroadcast;
boolean mFinished;
-
+
/** @hide */
public PendingResult(int resultCode, String resultData, Bundle resultExtras,
- int type, boolean ordered, boolean sticky, IBinder token) {
+ int type, boolean ordered, boolean sticky, IBinder token, int userId) {
mResultCode = resultCode;
mResultData = resultData;
mResultExtras = resultExtras;
@@ -254,6 +255,7 @@
mOrderedHint = ordered;
mInitialStickyHint = sticky;
mToken = token;
+ mSendingUser = userId;
}
/**
@@ -425,7 +427,12 @@
}
}
}
-
+
+ /** @hide */
+ public int getSendingUserId() {
+ return mSendingUser;
+ }
+
void checkSynchronousHint() {
// Note that we don't assert when receiving the initial sticky value,
// since that may have come from an ordered broadcast. We'll catch
@@ -733,6 +740,11 @@
return mPendingResult;
}
+ /** @hide */
+ public int getSendingUserId() {
+ return mPendingResult.mSendingUser;
+ }
+
/**
* Control inclusion of debugging help for mismatched
* calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index dc6d93f..7438ba8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -35,6 +35,8 @@
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
+import android.view.WindowManager;
import java.io.File;
import java.io.FileInputStream;
@@ -1127,6 +1129,24 @@
public abstract void sendBroadcastAsUser(Intent intent, UserHandle user);
/**
+ * Same as {@link #sendBroadcast(Intent, String)}, but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param user UserHandle to send the intent to.
+ * @param receiverPermission (optional) String naming a permission that
+ * a receiver must hold in order to receive your broadcast.
+ * If null, no permission is required.
+ *
+ * @see #sendBroadcast(Intent, String)
+ */
+ public abstract void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission);
+
+ /**
* Same as
* {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)},
* but for a specific user. This broadcast
@@ -1139,6 +1159,9 @@
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
* @param user UserHandle to send the intent to.
+ * @param receiverPermission String naming a permissions that
+ * a receiver must hold in order to receive your broadcast.
+ * If null, no permission is required.
* @param resultReceiver Your own BroadcastReceiver to treat as the final
* receiver of the broadcast.
* @param scheduler A custom Handler with which to schedule the
@@ -1154,7 +1177,7 @@
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
*/
public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- BroadcastReceiver resultReceiver, Handler scheduler,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras);
/**
@@ -1223,7 +1246,6 @@
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras);
-
/**
* Remove the data previously sent with {@link #sendStickyBroadcast},
* so that it is as if the sticky broadcast had never happened.
@@ -1239,6 +1261,73 @@
public abstract void removeStickyBroadcast(Intent intent);
/**
+ * Same as {@link #sendStickyBroadcast(Intent)},
+ * but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast, and the Intent will be held to
+ * be re-broadcast to future receivers.
+ * @param user UserHandle to send the intent to.
+ *
+ * @see #sendBroadcast(Intent)
+ */
+ public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user);
+
+ /**
+ * Same as
+ * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ * but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param user UserHandle to send the intent to.
+ * @param resultReceiver Your own BroadcastReceiver to treat as the final
+ * receiver of the broadcast.
+ * @param scheduler A custom Handler with which to schedule the
+ * resultReceiver callback; if null it will be
+ * scheduled in the Context's main thread.
+ * @param initialCode An initial value for the result code. Often
+ * Activity.RESULT_OK.
+ * @param initialData An initial value for the result data. Often
+ * null.
+ * @param initialExtras An initial value for the result extras. Often
+ * null.
+ *
+ * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ public abstract void sendStickyOrderedBroadcastAsUser(Intent intent,
+ UserHandle user, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras);
+
+ /**
+ * Same as
+ * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ * but for a specific user. This broadcast
+ * can only be sent to receivers that are part of the calling application. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * permission.
+ *
+ * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY}
+ * permission in order to use this API. If you do not hold that
+ * permission, {@link SecurityException} will be thrown.
+ *
+ * @param intent The Intent that was previously broadcast.
+ * @param user UserHandle to remove the sticky broadcast from.
+ *
+ * @see #sendStickyBroadcastAsUser
+ */
+ public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user);
+
+ /**
* Register a BroadcastReceiver to be run in the main activity thread. The
* <var>receiver</var> will be called with any broadcast Intent that
* matches <var>filter</var>, in the main application thread.
@@ -1321,9 +1410,35 @@
* @see #unregisterReceiver
*/
public abstract Intent registerReceiver(BroadcastReceiver receiver,
- IntentFilter filter,
- String broadcastPermission,
- Handler scheduler);
+ IntentFilter filter, String broadcastPermission, Handler scheduler);
+
+ /**
+ * @hide
+ * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+ * but for a specific user. This receiver will receiver broadcasts that
+ * are sent to the requested user. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission.
+ *
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param user UserHandle to send the intent to.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param broadcastPermission String naming a permissions that a
+ * broadcaster must hold in order to send an Intent to you. If null,
+ * no permission is required.
+ * @param scheduler Handler identifying the thread that will receive
+ * the Intent. If null, the main thread of the process will be used.
+ *
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ *
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler
+ * @see #sendBroadcast
+ * @see #unregisterReceiver
+ */
+ public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver,
+ UserHandle user, IntentFilter filter, String broadcastPermission,
+ Handler scheduler);
/**
* Unregister a previously registered BroadcastReceiver. <em>All</em>
@@ -2460,7 +2575,7 @@
/**
* Return a new Context object for the current Context but whose resources
* are adjusted to match the given Configuration. Each call to this method
- * returns a new instance of a Contex object; Context objects are not
+ * returns a new instance of a Context object; Context objects are not
* shared, however common state (ClassLoader, other Resources for the
* same configuration) may be so the Context itself can be fairly lightweight.
*
@@ -2470,19 +2585,40 @@
* orientation change), the resources of this context will also change except
* for those that have been explicitly overridden with a value here.
*
- * @return A Context for the application.
+ * @return A Context with the given configuration override.
*/
public abstract Context createConfigurationContext(Configuration overrideConfiguration);
/**
+ * Return a new Context object for the current Context but whose resources
+ * are adjusted to match the metrics of the given Display. Each call to this method
+ * returns a new instance of a Context object; Context objects are not
+ * shared, however common state (ClassLoader, other Resources for the
+ * same configuration) may be so the Context itself can be fairly lightweight.
+ *
+ * The returned display Context provides a {@link WindowManager}
+ * (see {@link #getSystemService(String)}) that is configured to show windows
+ * on the given display. The WindowManager's {@link WindowManager#getDefaultDisplay}
+ * method can be used to retrieve the Display from the returned Context.
+ *
+ * @param display A {@link Display} object specifying the display
+ * for whose metrics the Context's resources should be tailored and upon which
+ * new windows should be shown.
+ *
+ * @return A Context for the display.
+ */
+ public abstract Context createDisplayContext(Display display);
+
+ /**
* Gets the compatibility info holder for this context. This information
* is provided on a per-application basis and is used to simulate lower density
* display metrics for legacy applications.
*
+ * @param displayId The display id for which to get compatibility info.
* @return The compatibility info holder, or null if not required by the application.
* @hide
*/
- public abstract CompatibilityInfoHolder getCompatibilityInfo();
+ public abstract CompatibilityInfoHolder getCompatibilityInfo(int displayId);
/**
* Indicates whether this Context is restricted.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4bbe44e..6101f4e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,6 +16,9 @@
package android.content;
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.LoadedApk;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -30,8 +33,10 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@@ -354,10 +359,16 @@
}
@Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission) {
+ mBase.sendBroadcastAsUser(intent, user, receiverPermission);
+ }
+
+ @Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- BroadcastReceiver resultReceiver, Handler scheduler,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
- mBase.sendOrderedBroadcastAsUser(intent, user, resultReceiver,
+ mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
}
@@ -382,6 +393,25 @@
}
@Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ mBase.sendStickyBroadcastAsUser(intent, user);
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent,
+ UserHandle user, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ mBase.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver,
+ scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ mBase.removeStickyBroadcastAsUser(intent, user);
+ }
+
+ @Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
@@ -395,6 +425,15 @@
scheduler);
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(
+ BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
@@ -553,13 +592,18 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ return mBase.createDisplayContext(display);
+ }
+
+ @Override
public boolean isRestricted() {
return mBase.isRestricted();
}
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
- return mBase.getCompatibilityInfo();
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ return mBase.getCompatibilityInfo(displayId);
}
}
diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl
index 6f2f7c4..3d92723 100755
--- a/core/java/android/content/IIntentReceiver.aidl
+++ b/core/java/android/content/IIntentReceiver.aidl
@@ -27,7 +27,7 @@
* {@hide}
*/
oneway interface IIntentReceiver {
- void performReceive(in Intent intent, int resultCode,
- String data, in Bundle extras, boolean ordered, boolean sticky);
+ void performReceive(in Intent intent, int resultCode, String data,
+ in Bundle extras, boolean ordered, boolean sticky, int sendingUser);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 06edf32..bca5ade 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
-import android.media.RemoteControlClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -1642,6 +1641,15 @@
public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION";
/**
+ * Broadcast Action: Sent to the system package verifier when a package is
+ * verified. The data contains the package URI.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
+
+ /**
* Broadcast Action: Resources for a set of packages (which were
* previously unavailable) are currently
* available since the media on which they exist is available.
@@ -2287,6 +2295,15 @@
"android.intent.action.USER_ADDED";
/**
+ * Broadcast sent to the system when a user is stopped. Carries an extra EXTRA_USER_HANDLE that has
+ * the userHandle of the user. This is similar to {@link #ACTION_PACKAGE_RESTARTED},
+ * but for an entire user instead of a specific package.
+ * @hide
+ */
+ public static final String ACTION_USER_STOPPED =
+ "android.intent.action.USER_STOPPED";
+
+ /**
* Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
* the userHandle of the user.
* @hide
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 079241a..6c3cf99 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -114,8 +114,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index ee075b4..6b5e6e2 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -980,7 +980,7 @@
mSyncHandler.sendMessage(msg);
}
- boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {
+ boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
}
@@ -989,8 +989,9 @@
intent.setComponent(info.componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.sync_binding_label);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0));
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
+ mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
+ null, new UserHandle(userId)));
mBound = true;
final boolean bindResult = mContext.bindService(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
@@ -2132,7 +2133,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
}
- if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
+ if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
closeActiveSyncContext(activeSyncContext);
return false;
@@ -2255,10 +2256,12 @@
if (syncResult != null && syncResult.tooManyDeletions) {
installHandleTooManyDeletesNotification(syncOperation.account,
- syncOperation.authority, syncResult.stats.numDeletes);
+ syncOperation.authority, syncResult.stats.numDeletes,
+ syncOperation.userId);
} else {
- mNotificationMgr.cancel(
- syncOperation.account.hashCode() ^ syncOperation.authority.hashCode());
+ mNotificationMgr.cancelAsUser(null,
+ syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(),
+ new UserHandle(syncOperation.userId));
}
if (syncResult != null && syncResult.fullSyncRequested) {
@@ -2471,7 +2474,7 @@
}
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -2493,7 +2496,8 @@
}
final PendingIntent pendingIntent = PendingIntent
- .getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ .getActivityAsUser(mContext, 0, clickIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId));
CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
R.string.contentServiceTooManyDeletesNotificationDesc);
@@ -2507,7 +2511,8 @@
String.format(tooManyDeletesDescFormat.toString(), authorityName),
pendingIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
- mNotificationMgr.notify(account.hashCode() ^ authority.hashCode(), notification);
+ mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(),
+ notification, new UserHandle(userId));
}
/**
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 773e0fe..de97481 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,6 +25,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -336,6 +337,7 @@
private int mNextHistoryId = 0;
private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+ private boolean mDefaultMasterSyncAutomatically;
private OnSyncRequestListener mSyncRequestListener;
@@ -345,6 +347,9 @@
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+ mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
syncDir.mkdirs();
@@ -780,7 +785,7 @@
public boolean getMasterSyncAutomatically(int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
- return auto == null ? true : auto;
+ return auto == null ? mDefaultMasterSyncAutomatically : auto;
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3035729..0b320f0 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -372,6 +372,12 @@
public static final int CONFIG_DENSITY = 0x1000;
/**
* Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle the change to layout direction. Set from the
+ * {@link android.R.attr#configChanges} attribute.
+ */
+ public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
* not a core resource configutation, but a higher-level value, so its
@@ -398,6 +404,7 @@
0x0200, // SCREEN SIZE
0x2000, // SMALLEST SCREEN SIZE
0x0100, // DENSITY
+ 0x4000, // LAYOUT DIRECTION
};
/** @hide
* Convert Java change bits to native.
@@ -434,8 +441,9 @@
* {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
* {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
* {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
- * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the
- * {@link android.R.attr#configChanges} attribute.
+ * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT} 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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0d99d3f..e5ddfda 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1151,6 +1151,14 @@
= "android.content.pm.extra.VERIFICATION_INSTALL_FLAGS";
/**
+ * Extra field name for the result of a verification, either
+ * {@link #VERIFICATION_ALLOW}, or {@link #VERIFICATION_REJECT}.
+ * Passed to package verifiers after a package is verified.
+ */
+ public static final String EXTRA_VERIFICATION_RESULT
+ = "android.content.pm.extra.VERIFICATION_RESULT";
+
+ /**
* Retrieve overall information about an application package that is
* installed on the system.
* <p>
@@ -1796,6 +1804,26 @@
int flags);
/**
+ * Retrieve all receivers that can handle a broadcast of the given intent, for a specific
+ * user.
+ *
+ * @param intent The desired intent as per resolveActivity().
+ * @param flags Additional option flags.
+ * @param userId The userId of the user being queried.
+ *
+ * @return A List<ResolveInfo> containing one entry for each matching
+ * Receiver. These are ordered from first to last in priority. If
+ * there are no matching receivers, an empty list is returned.
+ *
+ * @see #MATCH_DEFAULT_ONLY
+ * @see #GET_INTENT_FILTERS
+ * @see #GET_RESOLVED_FILTER
+ * @hide
+ */
+ public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
+ int flags, int userId);
+
+ /**
* Determine the best service to handle for a given Intent.
*
* @param intent An intent containing all of the desired specification
@@ -2319,6 +2347,9 @@
* {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra
* @param verificationCode either {@link PackageManager#VERIFICATION_ALLOW}
* or {@link PackageManager#VERIFICATION_REJECT}.
+ * @throws SecurityException if the caller does not have the
+ * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT}
+ * permission.
*/
public abstract void verifyPendingInstall(int id, int verificationCode);
@@ -2338,13 +2369,22 @@
* {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra
* @param verificationCodeAtTimeout either
* {@link PackageManager#VERIFICATION_ALLOW} or
+ * {@link PackageManager#VERIFICATION_REJECT}. If
+ * {@code verificationCodeAtTimeout} is neither
+ * {@link PackageManager#VERIFICATION_ALLOW} or
+ * {@link PackageManager#VERIFICATION_REJECT}, then
+ * {@code verificationCodeAtTimeout} will default to
* {@link PackageManager#VERIFICATION_REJECT}.
* @param millisecondsToDelay the amount of time requested for the timeout.
* Must be positive and less than
+ * {@link PackageManager#MAXIMUM_VERIFICATION_TIMEOUT}. If
+ * {@code millisecondsToDelay} is out of bounds,
+ * {@code millisecondsToDelay} will be set to the closest in
+ * bounds value; namely, 0 or
* {@link PackageManager#MAXIMUM_VERIFICATION_TIMEOUT}.
- *
- * @throws IllegalArgumentException if {@code millisecondsToDelay} is out
- * of bounds or {@code verificationCodeAtTimeout} is unknown.
+ * @throws SecurityException if the caller does not have the
+ * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT}
+ * permission.
*/
public abstract void extendVerificationTimeout(int id,
int verificationCodeAtTimeout, long millisecondsToDelay);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 52b6498..7164713 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -125,7 +125,25 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
* resource qualifier. */
public static final int SCREENLAYOUT_LONG_YES = 0x20;
-
+
+ /** Constant for {@link #screenLayout}: bits that encode the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
+ /** Constant for {@link #screenLayout}: bits shift to get the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that no layout dir has been set. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0x00;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to LTR. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x01 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to RTL. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+
+ /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
+ public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
+ SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+
/**
* Special flag we generate to indicate that the screen layout requires
* us to use a compatibility mode for apps that are not modern layout
@@ -146,6 +164,10 @@
* is wider/taller than normal. They may be one of
* {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
*
+ * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
+ * is either LTR or RTL. They may be one of
+ * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.
+ *
* <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information.
*/
@@ -442,11 +464,6 @@
public int compatSmallestScreenWidthDp;
/**
- * @hide The layout direction associated to the current Locale
- */
- public int layoutDirection;
-
- /**
* @hide Internal book-keeping.
*/
public int seq;
@@ -472,7 +489,6 @@
mnc = o.mnc;
if (o.locale != null) {
locale = (Locale) o.locale.clone();
- layoutDirection = o.layoutDirection;
}
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
@@ -517,10 +533,13 @@
} else {
sb.append(" ?locale");
}
- switch (layoutDirection) {
- case View.LAYOUT_DIRECTION_LTR: /* ltr not interesting */ break;
- case View.LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
- default: sb.append(" layoutDir="); sb.append(layoutDirection); break;
+ int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
+ switch (layoutDir) {
+ case SCREENLAYOUT_LAYOUTDIR_UNDEFINED: sb.append(" ?layoutDir"); break;
+ case SCREENLAYOUT_LAYOUTDIR_LTR: sb.append(" ldltr"); break;
+ case SCREENLAYOUT_LAYOUTDIR_RTL: sb.append(" ldrtl"); break;
+ default: sb.append(" layoutDir=");
+ sb.append(layoutDir >> SCREENLAYOUT_LAYOUTDIR_SHIFT); break;
}
if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
@@ -643,13 +662,12 @@
navigation = NAVIGATION_UNDEFINED;
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
- screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
+ screenLayout = SCREENLAYOUT_UNDEFINED;
uiMode = UI_MODE_TYPE_UNDEFINED;
screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
- layoutDirection = View.LAYOUT_DIRECTION_LTR;
seq = 0;
}
@@ -685,7 +703,11 @@
changed |= ActivityInfo.CONFIG_LOCALE;
locale = delta.locale != null
? (Locale) delta.locale.clone() : null;
- layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+ // If locale has changed, then layout direction is also changed ...
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+ // ... and we need to update the layout direction (represented by the first
+ // 2 most significant bits in screenLayout).
+ setLayoutDirection(locale);
}
if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
{
@@ -727,10 +749,17 @@
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && (getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout))) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
- screenLayout = delta.screenLayout;
+ // We need to preserve the previous layout dir bits if they were defined
+ if ((delta.screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) == 0) {
+ screenLayout = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK)|delta.screenLayout;
+ } else {
+ screenLayout = delta.screenLayout;
+ }
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
@@ -771,7 +800,6 @@
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
-
if (delta.seq != 0) {
seq = delta.seq;
}
@@ -807,6 +835,8 @@
* PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
* {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
* PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
+ * {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
+ * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
*/
public int diff(Configuration delta) {
int changed = 0;
@@ -822,6 +852,7 @@
if (delta.locale != null
&& (locale == null || !locale.equals(delta.locale))) {
changed |= ActivityInfo.CONFIG_LOCALE;
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
}
if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
&& touchscreen != delta.touchscreen) {
@@ -851,8 +882,10 @@
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout)) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
@@ -875,7 +908,7 @@
&& densityDpi != delta.densityDpi) {
changed |= ActivityInfo.CONFIG_DENSITY;
}
-
+
return changed;
}
@@ -963,7 +996,6 @@
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeInt(layoutDirection);
dest.writeInt(seq);
}
@@ -992,7 +1024,6 @@
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- layoutDirection = source.readInt();
seq = source.readInt();
}
@@ -1100,4 +1131,50 @@
result = 31 * result + densityDpi;
return result;
}
+
+ /**
+ * Set the locale. This is the preferred way for setting up the locale (instead of using the
+ * direct accessor). This will also set the userLocale and layout direction according to
+ * the locale.
+ *
+ * @param loc The locale. Can be null.
+ */
+ public void setLocale(Locale loc) {
+ locale = loc;
+ userSetLocale = true;
+ setLayoutDirection(locale);
+ }
+
+ /**
+ * Return the layout direction. Will be either {@link View#LAYOUT_DIRECTION_LTR} or
+ * {@link View#LAYOUT_DIRECTION_RTL}.
+ *
+ * @return the layout direction
+ */
+ public int getLayoutDirection() {
+ // We need to substract one here as the configuration values are using "0" as undefined thus
+ // having LRT set to "1" and RTL set to "2"
+ return ((screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) >> SCREENLAYOUT_LAYOUTDIR_SHIFT) - 1;
+ }
+
+ /**
+ * Set the layout direction from the Locale.
+ *
+ * @param locale The Locale. If null will set the layout direction to
+ * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
+ * corresponding to the Locale.
+ *
+ * @see {@link View#LAYOUT_DIRECTION_LTR} and {@link View#LAYOUT_DIRECTION_RTL}
+ */
+ public void setLayoutDirection(Locale locale) {
+ // There is a "1" difference between the configuration values for
+ // layout direction and View constants for layout direction, just add "1".
+ final int layoutDirection = 1 + LocaleUtil.getLayoutDirectionFromLocale(locale);
+ screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
+ (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
+ }
+
+ private static int getScreenLayoutNoDirection(int screenLayout) {
+ return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
+ }
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7559f1e..b316f23 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1444,12 +1444,14 @@
}
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
+ mTmpConfig.setLayoutDirection(mTmpConfig.locale);
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
}
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLayoutDirection(mConfiguration.locale);
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
@@ -1895,12 +1897,14 @@
}
}
- final long key = (((long) value.assetCookie) << 32) | value.data;
boolean isColorDrawable = false;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
}
+ final long key = isColorDrawable ? value.data :
+ (((long) value.assetCookie) << 32) | value.data;
+
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
if (dr != null) {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4d9077f..829620b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.util.Log;
+import android.text.TextUtils;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -34,7 +35,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.StringTokenizer;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -1905,7 +1905,7 @@
private HashMap<String, String> mMap;
private Parameters() {
- mMap = new HashMap<String, String>();
+ mMap = new HashMap<String, String>(64);
}
/**
@@ -1929,7 +1929,7 @@
* semi-colon delimited key-value pairs
*/
public String flatten() {
- StringBuilder flattened = new StringBuilder();
+ StringBuilder flattened = new StringBuilder(128);
for (String k : mMap.keySet()) {
flattened.append(k);
flattened.append("=");
@@ -1952,9 +1952,9 @@
public void unflatten(String flattened) {
mMap.clear();
- StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
- while (tokenizer.hasMoreElements()) {
- String kv = tokenizer.nextToken();
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
+ splitter.setString(flattened);
+ for (String kv : splitter) {
int pos = kv.indexOf('=');
if (pos == -1) {
continue;
@@ -3488,11 +3488,11 @@
private ArrayList<String> split(String str) {
if (str == null) return null;
- // Use StringTokenizer because it is faster than split.
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<String> substrings = new ArrayList<String>();
- while (tokenizer.hasMoreElements()) {
- substrings.add(tokenizer.nextToken());
+ for (String s : splitter) {
+ substrings.add(s);
}
return substrings;
}
@@ -3502,11 +3502,11 @@
private ArrayList<Integer> splitInt(String str) {
if (str == null) return null;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<Integer> substrings = new ArrayList<Integer>();
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- substrings.add(Integer.parseInt(token));
+ for (String s : splitter) {
+ substrings.add(Integer.parseInt(s));
}
if (substrings.size() == 0) return null;
return substrings;
@@ -3515,11 +3515,11 @@
private void splitInt(String str, int[] output) {
if (str == null) return;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
int index = 0;
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- output[index++] = Integer.parseInt(token);
+ for (String s : splitter) {
+ output[index++] = Integer.parseInt(s);
}
}
@@ -3527,11 +3527,11 @@
private void splitFloat(String str, float[] output) {
if (str == null) return;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
int index = 0;
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- output[index++] = Float.parseFloat(token);
+ for (String s : splitter) {
+ output[index++] = Float.parseFloat(s);
}
}
@@ -3558,10 +3558,11 @@
private ArrayList<Size> splitSize(String str) {
if (str == null) return null;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<Size> sizeList = new ArrayList<Size>();
- while (tokenizer.hasMoreElements()) {
- Size size = strToSize(tokenizer.nextToken());
+ for (String s : splitter) {
+ Size size = strToSize(s);
if (size != null) sizeList.add(size);
}
if (sizeList.size() == 0) return null;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 98d2f69..2814301 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -18,20 +18,11 @@
import android.content.Context;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-import android.view.CompatibilityInfoHolder;
+import android.util.SparseArray;
import android.view.Display;
-import android.view.DisplayInfo;
-
-import java.util.ArrayList;
/**
- * Manages the properties, media routing and power state of attached displays.
+ * Manages the properties of attached displays.
* <p>
* Get an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String)
@@ -43,110 +34,71 @@
private static final String TAG = "DisplayManager";
private static final boolean DEBUG = false;
- private static final int MSG_DISPLAY_ADDED = 1;
- private static final int MSG_DISPLAY_REMOVED = 2;
- private static final int MSG_DISPLAY_CHANGED = 3;
+ private final Context mContext;
+ private final DisplayManagerGlobal mGlobal;
- private static DisplayManager sInstance;
+ private final Object mLock = new Object();
+ private final SparseArray<Display> mDisplays = new SparseArray<Display>();
- private final IDisplayManager mDm;
-
- // Guarded by mDisplayLock
- private final Object mDisplayLock = new Object();
- private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
- new ArrayList<DisplayListenerDelegate>();
-
-
- private DisplayManager(IDisplayManager dm) {
- mDm = dm;
+ /** @hide */
+ public DisplayManager(Context context) {
+ mContext = context;
+ mGlobal = DisplayManagerGlobal.getInstance();
}
/**
- * Gets an instance of the display manager.
+ * Gets information about a logical display.
*
- * @return The display manager instance, may be null early in system startup
- * before the display manager has been fully initialized.
+ * The display metrics may be adjusted to provide compatibility
+ * for legacy applications.
*
- * @hide
+ * @param displayId The logical display id.
+ * @return The display object, or null if there is no valid display with the given id.
*/
- public static DisplayManager getInstance() {
- synchronized (DisplayManager.class) {
- if (sInstance == null) {
- IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
- if (b != null) {
- sInstance = new DisplayManager(IDisplayManager.Stub.asInterface(b));
+ public Display getDisplay(int displayId) {
+ synchronized (mLock) {
+ return getOrCreateDisplayLocked(displayId, false /*assumeValid*/);
+ }
+ }
+
+ /**
+ * Gets all currently valid logical displays.
+ *
+ * @return An array containing all displays.
+ */
+ public Display[] getDisplays() {
+ int[] displayIds = mGlobal.getDisplayIds();
+ int expectedCount = displayIds.length;
+ Display[] displays = new Display[expectedCount];
+ synchronized (mLock) {
+ int actualCount = 0;
+ for (int i = 0; i < expectedCount; i++) {
+ Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
+ if (display != null) {
+ displays[actualCount++] = display;
}
}
- return sInstance;
+ if (actualCount != expectedCount) {
+ Display[] oldDisplays = displays;
+ displays = new Display[actualCount];
+ System.arraycopy(oldDisplays, 0, displays, 0, actualCount);
+ }
}
+ return displays;
}
- /**
- * Get information about a particular logical display.
- *
- * @param displayId The logical display id.
- * @param outInfo A structure to populate with the display info.
- * @return True if the logical display exists, false otherwise.
- * @hide
- */
- public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) {
- try {
- return mDm.getDisplayInfo(displayId, outInfo);
- } catch (RemoteException ex) {
- Log.e(TAG, "Could not get display information from display manager.", ex);
- return false;
+ private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
+ Display display = mDisplays.get(displayId);
+ if (display == null) {
+ display = mGlobal.getCompatibleDisplay(displayId,
+ mContext.getCompatibilityInfo(displayId));
+ if (display != null) {
+ mDisplays.put(displayId, display);
+ }
+ } else if (!assumeValid && !display.isValid()) {
+ display = null;
}
- }
-
- /**
- * Gets information about a logical display.
- *
- * The display metrics may be adjusted to provide compatibility
- * for legacy applications.
- *
- * @param displayId The logical display id.
- * @param applicationContext The application context from which to obtain
- * compatible metrics.
- * @return The display object.
- */
- public Display getDisplay(int displayId, Context applicationContext) {
- if (applicationContext == null) {
- throw new IllegalArgumentException("applicationContext must not be null");
- }
-
- CompatibilityInfoHolder cih = null;
- if (displayId == Display.DEFAULT_DISPLAY) {
- cih = applicationContext.getCompatibilityInfo();
- }
- return getCompatibleDisplay(displayId, cih);
- }
-
- /**
- * Gets information about a logical display.
- *
- * The display metrics may be adjusted to provide compatibility
- * for legacy applications.
- *
- * @param displayId The logical display id.
- * @param cih The compatibility info, or null if none is required.
- * @return The display object.
- *
- * @hide
- */
- public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) {
- return new Display(displayId, cih);
- }
-
- /**
- * Gets information about a logical display without applying any compatibility metrics.
- *
- * @param displayId The logical display id.
- * @return The display object.
- *
- * @hide
- */
- public Display getRealDisplay(int displayId) {
- return getCompatibleDisplay(displayId, null);
+ return display;
}
/**
@@ -160,16 +112,7 @@
* @see #unregisterDisplayListener
*/
public void registerDisplayListener(DisplayListener listener, Handler handler) {
- if (listener == null) {
- throw new IllegalArgumentException("listener must not be null");
- }
-
- synchronized (mDisplayLock) {
- int index = findDisplayListenerLocked(listener);
- if (index < 0) {
- mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
- }
- }
+ mGlobal.registerDisplayListener(listener, handler);
}
/**
@@ -180,28 +123,7 @@
* @see #registerDisplayListener
*/
public void unregisterDisplayListener(DisplayListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener must not be null");
- }
-
- synchronized (mDisplayLock) {
- int index = findDisplayListenerLocked(listener);
- if (index >= 0) {
- DisplayListenerDelegate d = mDisplayListeners.get(index);
- d.removeCallbacksAndMessages(null);
- mDisplayListeners.remove(index);
- }
- }
- }
-
- private int findDisplayListenerLocked(DisplayListener listener) {
- final int numListeners = mDisplayListeners.size();
- for (int i = 0; i < numListeners; i++) {
- if (mDisplayListeners.get(i).mListener == listener) {
- return i;
- }
- }
- return -1;
+ mGlobal.unregisterDisplayListener(listener);
}
/**
@@ -210,7 +132,8 @@
public interface DisplayListener {
/**
* Called whenever a logical display has been added to the system.
- * Use {@link DisplayManager#getDisplay} to get more information about the display.
+ * Use {@link DisplayManager#getDisplay} to get more information about
+ * the display.
*
* @param displayId The id of the logical display that was added.
*/
@@ -230,28 +153,4 @@
*/
void onDisplayChanged(int displayId);
}
-
- private static final class DisplayListenerDelegate extends Handler {
- public final DisplayListener mListener;
-
- public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
- super(handler != null ? handler.getLooper() : Looper.myLooper());
- mListener = listener;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DISPLAY_ADDED:
- mListener.onDisplayAdded(msg.arg1);
- break;
- case MSG_DISPLAY_REMOVED:
- mListener.onDisplayRemoved(msg.arg1);
- break;
- case MSG_DISPLAY_CHANGED:
- mListener.onDisplayChanged(msg.arg1);
- break;
- }
- }
- }
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
new file mode 100644
index 0000000..4077964
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.hardware.display;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.CompatibilityInfoHolder;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Manager communication with the display manager service on behalf of
+ * an application process. You're probably looking for {@link DisplayManager}.
+ *
+ * @hide
+ */
+public final class DisplayManagerGlobal {
+ private static final String TAG = "DisplayManager";
+ private static final boolean DEBUG = false;
+
+ // True if display info and display ids should be cached.
+ //
+ // FIXME: The cache is currently disabled because it's unclear whether we have the
+ // necessary guarantees that the caches will always be flushed before clients
+ // attempt to observe their new state. For example, depending on the order
+ // in which the binder transactions take place, we might have a problem where
+ // an application could start processing a configuration change due to a display
+ // orientation change before the display info cache has actually been invalidated.
+ private static final boolean USE_CACHE = false;
+
+ public static final int EVENT_DISPLAY_ADDED = 1;
+ public static final int EVENT_DISPLAY_CHANGED = 2;
+ public static final int EVENT_DISPLAY_REMOVED = 3;
+
+ private static DisplayManagerGlobal sInstance;
+
+ private final Object mLock = new Object();
+
+ private final IDisplayManager mDm;
+
+ private DisplayManagerCallback mCallback;
+ private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
+ new ArrayList<DisplayListenerDelegate>();
+
+ private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
+ private int[] mDisplayIdCache;
+
+ private DisplayManagerGlobal(IDisplayManager dm) {
+ mDm = dm;
+ }
+
+ /**
+ * Gets an instance of the display manager global singleton.
+ *
+ * @return The display manager instance, may be null early in system startup
+ * before the display manager has been fully initialized.
+ */
+ public static DisplayManagerGlobal getInstance() {
+ synchronized (DisplayManagerGlobal.class) {
+ if (sInstance == null) {
+ IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
+ if (b != null) {
+ sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Get information about a particular logical display.
+ *
+ * @param displayId The logical display id.
+ * @return Information about the specified display, or null if it does not exist.
+ * This object belongs to an internal cache and should be treated as if it were immutable.
+ */
+ public DisplayInfo getDisplayInfo(int displayId) {
+ try {
+ synchronized (mLock) {
+ DisplayInfo info;
+ if (USE_CACHE) {
+ info = mDisplayInfoCache.get(displayId);
+ if (info != null) {
+ return info;
+ }
+ }
+
+ info = mDm.getDisplayInfo(displayId);
+ if (info == null) {
+ return null;
+ }
+
+ if (USE_CACHE) {
+ mDisplayInfoCache.put(displayId, info);
+ }
+ registerCallbackIfNeededLocked();
+
+ if (DEBUG) {
+ Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
+ }
+ return info;
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Could not get display information from display manager.", ex);
+ return null;
+ }
+ }
+
+ /**
+ * Gets all currently valid logical display ids.
+ *
+ * @return An array containing all display ids.
+ */
+ public int[] getDisplayIds() {
+ try {
+ synchronized (mLock) {
+ if (USE_CACHE) {
+ if (mDisplayIdCache != null) {
+ return mDisplayIdCache;
+ }
+ }
+
+ int[] displayIds = mDm.getDisplayIds();
+ if (USE_CACHE) {
+ mDisplayIdCache = displayIds;
+ }
+ registerCallbackIfNeededLocked();
+ return displayIds;
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Could not get display ids from display manager.", ex);
+ return new int[] { Display.DEFAULT_DISPLAY };
+ }
+ }
+
+ /**
+ * Gets information about a logical display.
+ *
+ * The display metrics may be adjusted to provide compatibility
+ * for legacy applications.
+ *
+ * @param displayId The logical display id.
+ * @param cih The compatibility info, or null if none is required.
+ * @return The display object, or null if there is no display with the given id.
+ */
+ public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) {
+ DisplayInfo displayInfo = getDisplayInfo(displayId);
+ if (displayInfo == null) {
+ return null;
+ }
+ return new Display(this, displayId, displayInfo, cih);
+ }
+
+ /**
+ * Gets information about a logical display without applying any compatibility metrics.
+ *
+ * @param displayId The logical display id.
+ * @return The display object, or null if there is no display with the given id.
+ */
+ public Display getRealDisplay(int displayId) {
+ return getCompatibleDisplay(displayId, null);
+ }
+
+ public void registerDisplayListener(DisplayListener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mLock) {
+ int index = findDisplayListenerLocked(listener);
+ if (index < 0) {
+ mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
+ registerCallbackIfNeededLocked();
+ }
+ }
+ }
+
+ public void unregisterDisplayListener(DisplayListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mLock) {
+ int index = findDisplayListenerLocked(listener);
+ if (index >= 0) {
+ DisplayListenerDelegate d = mDisplayListeners.get(index);
+ d.clearEvents();
+ mDisplayListeners.remove(index);
+ }
+ }
+ }
+
+ private int findDisplayListenerLocked(DisplayListener listener) {
+ final int numListeners = mDisplayListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (mDisplayListeners.get(i).mListener == listener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private void registerCallbackIfNeededLocked() {
+ if (mCallback == null) {
+ mCallback = new DisplayManagerCallback();
+ try {
+ mDm.registerCallback(mCallback);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to register callback with display manager service.", ex);
+ mCallback = null;
+ }
+ }
+ }
+
+ private void handleDisplayEvent(int displayId, int event) {
+ synchronized (mLock) {
+ if (USE_CACHE) {
+ mDisplayInfoCache.remove(displayId);
+
+ if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
+ mDisplayIdCache = null;
+ }
+ }
+
+ final int numListeners = mDisplayListeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
+ }
+ }
+ }
+
+ private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
+ @Override
+ public void onDisplayEvent(int displayId, int event) {
+ if (DEBUG) {
+ Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
+ }
+ handleDisplayEvent(displayId, event);
+ }
+ }
+
+ private static final class DisplayListenerDelegate extends Handler {
+ public final DisplayListener mListener;
+
+ public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
+ super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
+ mListener = listener;
+ }
+
+ public void sendDisplayEvent(int displayId, int event) {
+ Message msg = obtainMessage(event, displayId, 0);
+ sendMessage(msg);
+ }
+
+ public void clearEvents() {
+ removeCallbacksAndMessages(null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_DISPLAY_ADDED:
+ mListener.onDisplayAdded(msg.arg1);
+ break;
+ case EVENT_DISPLAY_CHANGED:
+ mListener.onDisplayChanged(msg.arg1);
+ break;
+ case EVENT_DISPLAY_REMOVED:
+ mListener.onDisplayRemoved(msg.arg1);
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index fd8c35f..d802aa1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,9 +16,13 @@
package android.hardware.display;
+import android.hardware.display.IDisplayManagerCallback;
import android.view.DisplayInfo;
/** @hide */
interface IDisplayManager {
- boolean getDisplayInfo(int displayId, out DisplayInfo outInfo);
+ DisplayInfo getDisplayInfo(int displayId);
+ int[] getDisplayIds();
+
+ void registerCallback(in IDisplayManagerCallback callback);
}
diff --git a/core/java/android/hardware/display/IDisplayManagerCallback.aidl b/core/java/android/hardware/display/IDisplayManagerCallback.aidl
new file mode 100644
index 0000000..c50e3fb
--- /dev/null
+++ b/core/java/android/hardware/display/IDisplayManagerCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.hardware.display;
+
+/** @hide */
+interface IDisplayManagerCallback {
+ oneway void onDisplayEvent(int displayId, int event);
+}
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 99bd647..4b60f07 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -96,6 +96,11 @@
}
@Override
+ public void captivePortalCheckComplete() {
+ // not implemented
+ }
+
+ @Override
public boolean setRadio(boolean turnOn) {
// Base tracker doesn't handle radios
return true;
diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java
new file mode 100644
index 0000000..aa392d0
--- /dev/null
+++ b/core/java/android/net/CaptivePortalTracker.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.net;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.android.internal.R;
+
+/**
+ * This class allows captive portal detection
+ * @hide
+ */
+public class CaptivePortalTracker {
+ private static final boolean DBG = true;
+ private static final String TAG = "CaptivePortalTracker";
+
+ private static final String DEFAULT_SERVER = "clients3.google.com";
+ private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
+
+ private static final int SOCKET_TIMEOUT_MS = 10000;
+
+ private String mServer;
+ private String mUrl;
+ private boolean mNotificationShown = false;
+ private boolean mIsCaptivePortalCheckEnabled = false;
+ private InternalHandler mHandler;
+ private IConnectivityManager mConnService;
+ private Context mContext;
+ private NetworkInfo mNetworkInfo;
+ private boolean mIsCaptivePortal = false;
+
+ private static final int DETECT_PORTAL = 0;
+ private static final int HANDLE_CONNECT = 1;
+
+ /**
+ * Activity Action: Switch to the captive portal network
+ * <p>Input: Nothing.
+ * <p>Output: Nothing.
+ */
+ public static final String ACTION_SWITCH_TO_CAPTIVE_PORTAL
+ = "android.net.SWITCH_TO_CAPTIVE_PORTAL";
+
+ private CaptivePortalTracker(Context context, NetworkInfo info, IConnectivityManager cs) {
+ mContext = context;
+ mNetworkInfo = info;
+ mConnService = cs;
+
+ HandlerThread handlerThread = new HandlerThread("CaptivePortalThread");
+ handlerThread.start();
+ mHandler = new InternalHandler(handlerThread.getLooper());
+ mHandler.obtainMessage(DETECT_PORTAL).sendToTarget();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SWITCH_TO_CAPTIVE_PORTAL);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+
+ mContext.registerReceiver(mReceiver, filter);
+
+ mServer = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.CAPTIVE_PORTAL_SERVER);
+ if (mServer == null) mServer = DEFAULT_SERVER;
+
+ mIsCaptivePortalCheckEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ACTION_SWITCH_TO_CAPTIVE_PORTAL)) {
+ notifyPortalCheckComplete();
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ NetworkInfo info = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ mHandler.obtainMessage(HANDLE_CONNECT, info).sendToTarget();
+ }
+ }
+ };
+
+ public static CaptivePortalTracker detect(Context context, NetworkInfo info,
+ IConnectivityManager cs) {
+ CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, info, cs);
+ return captivePortal;
+ }
+
+ private class InternalHandler extends Handler {
+ public InternalHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DETECT_PORTAL:
+ InetAddress server = lookupHost(mServer);
+ if (server != null) {
+ requestRouteToHost(server);
+ if (isCaptivePortal(server)) {
+ if (DBG) log("Captive portal " + mNetworkInfo);
+ setNotificationVisible(true);
+ mIsCaptivePortal = true;
+ break;
+ }
+ }
+ notifyPortalCheckComplete();
+ quit();
+ break;
+ case HANDLE_CONNECT:
+ NetworkInfo info = (NetworkInfo) msg.obj;
+ if (info.getType() != mNetworkInfo.getType()) break;
+
+ if (info.getState() == NetworkInfo.State.CONNECTED ||
+ info.getState() == NetworkInfo.State.DISCONNECTED) {
+ setNotificationVisible(false);
+ }
+
+ /* Connected to a captive portal */
+ if (info.getState() == NetworkInfo.State.CONNECTED &&
+ mIsCaptivePortal) {
+ launchBrowser();
+ quit();
+ }
+ break;
+ default:
+ loge("Unhandled message " + msg);
+ break;
+ }
+ }
+
+ private void quit() {
+ mIsCaptivePortal = false;
+ getLooper().quit();
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ private void launchBrowser() {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+
+ private void notifyPortalCheckComplete() {
+ try {
+ mConnService.captivePortalCheckComplete(mNetworkInfo);
+ } catch(RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void requestRouteToHost(InetAddress server) {
+ try {
+ mConnService.requestRouteToHostAddress(mNetworkInfo.getType(),
+ server.getAddress());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Do a URL fetch on a known server to see if we get the data we expect
+ */
+ private boolean isCaptivePortal(InetAddress server) {
+ HttpURLConnection urlConnection = null;
+ if (!mIsCaptivePortalCheckEnabled) return false;
+
+ mUrl = "http://" + server.getHostAddress() + "/generate_204";
+ try {
+ URL url = new URL(mUrl);
+ urlConnection = (HttpURLConnection) url.openConnection();
+ urlConnection.setInstanceFollowRedirects(false);
+ urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setUseCaches(false);
+ urlConnection.getInputStream();
+ // we got a valid response, but not from the real google
+ return urlConnection.getResponseCode() != 204;
+ } catch (IOException e) {
+ if (DBG) log("Probably not a portal: exception " + e);
+ return false;
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ }
+ }
+
+ private InetAddress lookupHost(String hostname) {
+ InetAddress inetAddress[];
+ try {
+ inetAddress = InetAddress.getAllByName(hostname);
+ } catch (UnknownHostException e) {
+ return null;
+ }
+
+ for (InetAddress a : inetAddress) {
+ if (a instanceof Inet4Address) return a;
+ }
+ return null;
+ }
+
+ private void setNotificationVisible(boolean visible) {
+ // if it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown) {
+ return;
+ }
+
+ Resources r = Resources.getSystem();
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (visible) {
+ CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
+ CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
+ mNetworkInfo.getExtraInfo());
+
+ Notification notification = new Notification();
+ notification.when = 0;
+ notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ notification.flags = Notification.FLAG_AUTO_CANCEL;
+ notification.contentIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(CaptivePortalTracker.ACTION_SWITCH_TO_CAPTIVE_PORTAL), 0);
+
+ notification.tickerText = title;
+ notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
+
+ notificationManager.notify(NOTIFICATION_ID, 1, notification);
+ } else {
+ notificationManager.cancel(NOTIFICATION_ID, 1);
+ }
+ mNotificationShown = visible;
+ }
+
+ private static void log(String s) {
+ Log.d(TAG, s);
+ }
+
+ private static void loge(String s) {
+ Log.e(TAG, s);
+ }
+
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 60bf4d6..a570473 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -921,4 +921,15 @@
return false;
}
}
+
+ /**
+ * {@hide}
+ */
+ public void captivePortalCheckComplete(NetworkInfo info) {
+ try {
+ mService.captivePortalCheckComplete(info);
+ } catch (RemoteException e) {
+ }
+ }
+
}
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index cc3e34f..874e80a 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -92,10 +92,12 @@
/* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
* success/failure */
public static final int CMD_POST_DHCP_ACTION = BASE + 5;
+ /* Notification from DHCP state machine before quitting */
+ public static final int CMD_ON_QUIT = BASE + 6;
/* Command from controller to indicate DHCP discovery/renewal can continue
* after pre DHCP action is complete */
- public static final int CMD_PRE_DHCP_ACTION_COMPLETE = BASE + 6;
+ public static final int CMD_PRE_DHCP_ACTION_COMPLETE = BASE + 7;
/* Message.arg1 arguments to CMD_POST_DHCP notification */
public static final int DHCP_SUCCESS = 1;
@@ -172,6 +174,10 @@
quit();
}
+ protected void onQuitting() {
+ mController.sendMessage(CMD_ON_QUIT);
+ }
+
class DefaultState extends State {
@Override
public void exit() {
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index ccd96ff..39440c2 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -119,6 +119,10 @@
return true;
}
+ public void captivePortalCheckComplete() {
+ // not implemented
+ }
+
/**
* Record the detailed state of a network, and if it is a
* change from the previous state, send a notification to
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index c690430..c52aa9e 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -274,6 +274,11 @@
return mLinkUp;
}
+ @Override
+ public void captivePortalCheckComplete() {
+ // not implemented
+ }
+
/**
* Turn the wireless radio off for a network.
* @param turnOn {@code true} to turn the radio on, {@code false}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3614045..056fa03 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -124,4 +124,6 @@
LegacyVpnInfo getLegacyVpnInfo();
boolean updateLockdownVpn();
+
+ void captivePortalCheckComplete(in NetworkInfo info);
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index d59fa6a..b35d61c 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -381,6 +381,11 @@
return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
}
+ @Override
+ public void captivePortalCheckComplete() {
+ // not implemented
+ }
+
/**
* Record the detailed state of a network, and if it is a
* change from the previous state, send a notification to
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 0bc6b58..0b23cb7 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -79,7 +79,9 @@
/** Access to this network is blocked. */
BLOCKED,
/** Link has poor connectivity. */
- VERIFYING_POOR_LINK
+ VERIFYING_POOR_LINK,
+ /** Checking if network is a captive portal */
+ CAPTIVE_PORTAL_CHECK,
}
/**
@@ -97,6 +99,7 @@
stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
+ stateMap.put(DetailedState.CAPTIVE_PORTAL_CHECK, State.CONNECTING);
stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index eae89f1..0a0c1e0 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -123,6 +123,11 @@
public boolean reconnect();
/**
+ * Ready to switch on to the network after captive portal check
+ */
+ public void captivePortalCheckComplete();
+
+ /**
* Turn the wireless radio off for a network.
* @param turnOn {@code true} to turn the radio on, {@code false}
*/
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fb5263d..65d3f2b 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -329,7 +329,7 @@
throw new IllegalArgumentException("Bad address");
}
- mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ mAddresses.append(' ' + address.getHostAddress() + '/' + prefixLength);
return this;
}
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index ed1c5b3..2d9dae9 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -688,7 +688,8 @@
}
} catch (FormatException e) { }
} else if (Arrays.equals(mType, RTD_URI)) {
- return parseWktUri().normalizeScheme();
+ Uri wktUri = parseWktUri();
+ return (wktUri != null ? wktUri.normalizeScheme() : null);
}
break;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 5d40456..ea14098 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -73,34 +73,6 @@
public static final native int getCallingUid();
/**
- * Return the original ID of the user assigned to the process that sent you the current
- * transaction that is being processed. This uid can be used with higher-level system services
- * to determine its identity and check permissions. If the current thread is not currently
- * executing an incoming transaction, then its own uid is returned.
- * <p/>
- * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
- * @hide
- */
- public static final int getOrigCallingUid() {
- if (UserHandle.MU_ENABLED) {
- return getOrigCallingUidNative();
- } else {
- return getCallingUid();
- }
- }
-
- private static final native int getOrigCallingUidNative();
-
- /**
- * Utility function to return the user id of the calling process.
- * @return userId of the calling process, extracted from the callingUid
- * @hide
- */
- public static final int getOrigCallingUser() {
- return UserHandle.getUserId(getOrigCallingUid());
- }
-
- /**
* Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
* on interfaces of other objects that may be local to your process and
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 679cf1a..2fbcf3f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -104,6 +104,17 @@
}
/**
+ * Return the system directory for a user. This is for use by system services to store
+ * files relating to the user. This directory will be automatically deleted when the user
+ * is removed.
+ *
+ * @hide
+ */
+ public static File getUserSystemDirectory(int userId) {
+ return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));
+ }
+
+ /**
* Returns whether the Encrypted File System feature is enabled on the device or not.
* @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
* if disabled.
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 4e2b5c0..94de448 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -412,6 +412,58 @@
}
/**
+ * Runs the specified task synchronously.
+ *
+ * If the current thread is the same as the handler thread, then the runnable
+ * runs immediately without being enqueued. Otherwise, posts the runnable
+ * to the handler and waits for it to complete before returning.
+ *
+ * This method is dangerous! Improper use can result in deadlocks.
+ * Never call this method while any locks are held or use it in a
+ * possibly re-entrant manner.
+ *
+ * This method is occasionally useful in situations where a background thread
+ * must synchronously await completion of a task that must run on the
+ * handler's thread. However, this problem is often a symptom of bad design.
+ * Consider improving the design (if possible) before resorting to this method.
+ *
+ * One example of where you might want to use this method is when you just
+ * set up a Handler thread and need to perform some initialization steps on
+ * it before continuing execution.
+ *
+ * If timeout occurs then this method returns <code>false</code> but the runnable
+ * will remain posted on the handler and may already be in progress or
+ * complete at a later time.
+ *
+ * @param r The Runnable that will be executed synchronously.
+ * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
+ *
+ * @return Returns true if the Runnable was successfully executed.
+ * Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ *
+ * @hide This method is prone to abuse and should probably not be in the API.
+ * If we ever do make it part of the API, we might want to rename it to something
+ * less funny like runUnsafe().
+ */
+ public final boolean runWithScissors(final Runnable r, long timeout) {
+ if (r == null) {
+ throw new IllegalArgumentException("runnable must not be null");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout must be non-negative");
+ }
+
+ if (Looper.myLooper() == mLooper) {
+ r.run();
+ return true;
+ }
+
+ BlockingRunnable br = new BlockingRunnable(r);
+ return br.postAndWait(this, timeout);
+ }
+
+ /**
* Remove any pending posts of Runnable r that are in the message queue.
*/
public final void removeCallbacks(Runnable r)
@@ -678,4 +730,55 @@
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
+
+ private static final class BlockingRunnable implements Runnable {
+ private final Runnable mTask;
+ private boolean mDone;
+
+ public BlockingRunnable(Runnable task) {
+ mTask = task;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mTask.run();
+ } finally {
+ synchronized (this) {
+ mDone = true;
+ notifyAll();
+ }
+ }
+ }
+
+ public boolean postAndWait(Handler handler, long timeout) {
+ if (!handler.post(this)) {
+ return false;
+ }
+
+ synchronized (this) {
+ if (timeout > 0) {
+ final long expirationTime = SystemClock.uptimeMillis() + timeout;
+ while (!mDone) {
+ long delay = expirationTime - SystemClock.uptimeMillis();
+ if (delay <= 0) {
+ return false; // timeout
+ }
+ try {
+ wait(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ } else {
+ while (!mDone) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+ return true;
+ }
+ }
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 851b8df..d5fca4d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -584,6 +584,8 @@
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
argsForZygote.add("--mount-external-multiuser");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
+ argsForZygote.add("--mount-external-multiuser-all");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 0d11ab4..480fe7d 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -348,7 +348,8 @@
final ConditionVariable condition = new ConditionVariable();
Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
- context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR,
+ context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ android.Manifest.permission.MASTER_CLEAR,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b74af16..d02a320 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -304,4 +304,25 @@
mBroadcastCount = -1;
}
+
+ /**
+ * Returns the number of registered callbacks. Note that the number of registered
+ * callbacks may differ from the value returned by {@link #beginBroadcast()} since
+ * the former returns the number of callbacks registered at the time of the call
+ * and the second the number of callback to which the broadcast will be delivered.
+ * <p>
+ * This function is useful to decide whether to schedule a broadcast if this
+ * requires doing some work which otherwise would not be performed.
+ * </p>
+ *
+ * @return The size.
+ */
+ public int getRegisteredCallbackCount() {
+ synchronized (mCallbacks) {
+ if (mKilled) {
+ return 0;
+ }
+ return mCallbacks.size();
+ }
+ }
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index d33bd80..fff7d5f 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -34,12 +34,28 @@
/** @hide A user id to indicate the currently active user */
public static final int USER_CURRENT = -2;
+ /** @hide A user handle to indicate the current user of the device */
+ public static final UserHandle CURRENT = new UserHandle(USER_CURRENT);
+
+ /** @hide A user id to indicate that we would like to send to the current
+ * user, but if this is calling from a user process then we will send it
+ * to the caller's user instead of failing wiht a security exception */
+ public static final int USER_CURRENT_OR_SELF = -2;
+
+ /** @hide A user handle to indicate that we would like to send to the current
+ * user, but if this is calling from a user process then we will send it
+ * to the caller's user instead of failing wiht a security exception */
+ public static final UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF);
+
/** @hide An undefined user id */
public static final int USER_NULL = -10000;
/** @hide A user id constant to indicate the "owner" user of the device */
public static final int USER_OWNER = 0;
+ /** @hide A user handle to indicate the primary/owner user of the device */
+ public static final UserHandle OWNER = new UserHandle(USER_OWNER);
+
/**
* @hide Enable multi-user related side effects. Set this to false if
* there are problems with single user use-cases.
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 79c8f3b..b5983d1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -26,16 +26,16 @@
*/
public class StorageVolume implements Parcelable {
- //private static final String TAG = "StorageVolume";
+ private int mStorageId;
private final String mPath;
private final int mDescriptionId;
+ private final boolean mPrimary;
private final boolean mRemovable;
private final boolean mEmulated;
private final int mMtpReserveSpace;
private final boolean mAllowMassStorage;
- private int mStorageId;
- // maximum file size for the storage, or zero for no limit
+ /** Maximum file size for the storage, or zero for no limit */
private final long mMaxFileSize;
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
@@ -43,10 +43,11 @@
// ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
- public StorageVolume(String path, int descriptionId, boolean removable,
+ public StorageVolume(String path, int descriptionId, boolean primary, boolean removable,
boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize) {
mPath = path;
mDescriptionId = descriptionId;
+ mPrimary = primary;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
@@ -54,18 +55,16 @@
mMaxFileSize = maxFileSize;
}
- // for parcelling only
- private StorageVolume(String path, int descriptionId, boolean removable,
- boolean emulated, int mtpReserveSpace, int storageId,
- boolean allowMassStorage, long maxFileSize) {
- mPath = path;
- mDescriptionId = descriptionId;
- mRemovable = removable;
- mEmulated = emulated;
- mMtpReserveSpace = mtpReserveSpace;
- mAllowMassStorage = allowMassStorage;
- mStorageId = storageId;
- mMaxFileSize = maxFileSize;
+ private StorageVolume(Parcel in) {
+ mStorageId = in.readInt();
+ mPath = in.readString();
+ mDescriptionId = in.readInt();
+ mPrimary = in.readByte() != 0;
+ mRemovable = in.readByte() != 0;
+ mEmulated = in.readByte() != 0;
+ mMtpReserveSpace = in.readInt();
+ mAllowMassStorage = in.readByte() != 0;
+ mMaxFileSize = in.readLong();
}
/**
@@ -90,6 +89,10 @@
return mDescriptionId;
}
+ public boolean isPrimary() {
+ return mPrimary;
+ }
+
/**
* Returns true if the volume is removable.
*
@@ -183,37 +186,31 @@
+ mRemovable + ", mStorageId=" + mStorageId + "]";
}
- public static final Parcelable.Creator<StorageVolume> CREATOR =
- new Parcelable.Creator<StorageVolume>() {
+ public static final Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() {
+ @Override
public StorageVolume createFromParcel(Parcel in) {
- String path = in.readString();
- int descriptionId = in.readInt();
- int removable = in.readInt();
- int emulated = in.readInt();
- int storageId = in.readInt();
- int mtpReserveSpace = in.readInt();
- int allowMassStorage = in.readInt();
- long maxFileSize = in.readLong();
- return new StorageVolume(path, descriptionId,
- removable == 1, emulated == 1, mtpReserveSpace,
- storageId, allowMassStorage == 1, maxFileSize);
+ return new StorageVolume(in);
}
+ @Override
public StorageVolume[] newArray(int size) {
return new StorageVolume[size];
}
};
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mStorageId);
parcel.writeString(mPath);
parcel.writeInt(mDescriptionId);
+ parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
- parcel.writeInt(mStorageId);
parcel.writeInt(mMtpReserveSpace);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
parcel.writeLong(mMaxFileSize);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f6f0dd..d7ae441 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -621,6 +621,25 @@
public static final String CALL_METHOD_GET_SECURE = "GET_secure";
/**
+ * @hide - Private call() method on SettingsProvider to read from 'global' table.
+ */
+ public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
+
+ /**
+ * @hide - User handle argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_USER_KEY = "_user";
+
+ /** @hide - Private call() method to write to 'system' table */
+ public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
+
+ /** @hide - Private call() method to write to 'secure' table */
+ public static final String CALL_METHOD_PUT_SECURE = "PUT_secure";
+
+ /** @hide - Private call() method to write to 'global' table */
+ public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global";
+
+ /**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
* This can be passed as an extra field in an Activity Intent with one or more syncable content
@@ -640,7 +659,7 @@
public static final String AUTHORITY = "settings";
private static final String TAG = "Settings";
- private static final boolean LOCAL_LOGV = false || false;
+ private static final boolean LOCAL_LOGV = false;
public static class SettingNotFoundException extends AndroidException {
public SettingNotFoundException(String msg) {
@@ -693,20 +712,52 @@
// The method we'll call (or null, to not use) on the provider
// for the fast path of retrieving settings.
- private final String mCallCommand;
+ private final String mCallGetCommand;
+ private final String mCallSetCommand;
- public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) {
+ public NameValueCache(String versionSystemProperty, Uri uri,
+ String getCommand, String setCommand) {
mVersionSystemProperty = versionSystemProperty;
mUri = uri;
- mCallCommand = callCommand;
+ mCallGetCommand = getCommand;
+ mCallSetCommand = setCommand;
}
- public String getString(ContentResolver cr, String name) {
+ private IContentProvider lazyGetProvider(ContentResolver cr) {
+ IContentProvider cp = null;
+ synchronized (this) {
+ cp = mContentProvider;
+ if (cp == null) {
+ cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
+ }
+ }
+ return cp;
+ }
+
+ public boolean putStringForUser(ContentResolver cr, String name, String value,
+ final int userHandle) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putString(Settings.NameValueTable.VALUE, value);
+ IContentProvider cp = lazyGetProvider(cr);
+ cp.call(mCallSetCommand, name, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean putString(ContentResolver cr, String name, String value) {
+ return putStringForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
synchronized (this) {
if (mValuesVersion != newValuesVersion) {
- if (LOCAL_LOGV) {
+ if (LOCAL_LOGV || false) {
Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " +
newValuesVersion + " != cached " + mValuesVersion);
}
@@ -720,21 +771,20 @@
}
}
- IContentProvider cp = null;
- synchronized (this) {
- cp = mContentProvider;
- if (cp == null) {
- cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
- }
- }
+ IContentProvider cp = lazyGetProvider(cr);
// Try the fast path first, not using query(). If this
// fails (alternate Settings provider that doesn't support
// this interface?) then we fall back to the query/table
// interface.
- if (mCallCommand != null) {
+ if (mCallGetCommand != null) {
try {
- Bundle b = cp.call(mCallCommand, name, null);
+ Bundle args = null;
+ if (userHandle != UserHandle.myUserId()) {
+ args = new Bundle();
+ args.putInt(CALL_METHOD_USER_KEY, userHandle);
+ }
+ Bundle b = cp.call(mCallGetCommand, name, args);
if (b != null) {
String value = b.getPairValue();
synchronized (this) {
@@ -775,6 +825,10 @@
if (c != null) c.close();
}
}
+
+ public String getString(ContentResolver cr, String name) {
+ return getStringForUser(cr, name, UserHandle.myUserId());
+ }
}
/**
@@ -791,13 +845,8 @@
private static final HashSet<String> MOVED_TO_SECURE;
static {
MOVED_TO_SECURE = new HashSet<String>(30);
- MOVED_TO_SECURE.add(Secure.ADB_ENABLED);
MOVED_TO_SECURE.add(Secure.ANDROID_ID);
- MOVED_TO_SECURE.add(Secure.BLUETOOTH_ON);
- MOVED_TO_SECURE.add(Secure.DATA_ROAMING);
- MOVED_TO_SECURE.add(Secure.DEVICE_PROVISIONED);
MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
- MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS);
MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
MOVED_TO_SECURE.add(Secure.LOCK_BIOMETRIC_WEAK_FLAGS);
MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_ENABLED);
@@ -808,7 +857,6 @@
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_LAST_UPDATE);
MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_REDIRECT_URL);
MOVED_TO_SECURE.add(Secure.SETTINGS_CLASSNAME);
- MOVED_TO_SECURE.add(Secure.USB_MASS_STORAGE_ENABLED);
MOVED_TO_SECURE.add(Secure.USE_GOOGLE_MAIL);
MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
@@ -827,6 +875,44 @@
MOVED_TO_SECURE.add(Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS);
}
+ private static final HashSet<String> MOVED_TO_GLOBAL;
+ static {
+ MOVED_TO_GLOBAL = new HashSet<String>();
+ MOVED_TO_GLOBAL.add(Global.ADB_ENABLED);
+ MOVED_TO_GLOBAL.add(Global.BLUETOOTH_ON);
+ MOVED_TO_GLOBAL.add(Global.DATA_ROAMING);
+ MOVED_TO_GLOBAL.add(Global.DEVICE_PROVISIONED);
+ MOVED_TO_GLOBAL.add(Global.INSTALL_NON_MARKET_APPS);
+ MOVED_TO_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
+
+ MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_RADIOS);
+ MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ MOVED_TO_GLOBAL.add(Settings.Global.AUTO_TIME);
+ MOVED_TO_GLOBAL.add(Settings.Global.AUTO_TIME_ZONE);
+ MOVED_TO_GLOBAL.add(Settings.Global.CAR_DOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.CAR_UNDOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.DESK_DOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.DESK_UNDOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.DOCK_SOUNDS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.LOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.UNLOCK_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.LOW_BATTERY_SOUND);
+ MOVED_TO_GLOBAL.add(Settings.Global.POWER_SOUNDS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SLEEP_POLICY);
+ }
+
+ private static void lazyInitCache() {
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(
+ SYS_PROP_SETTING_VERSION + '_' + UserHandle.myUserId(),
+ CONTENT_URI,
+ CALL_METHOD_GET_SYSTEM,
+ CALL_METHOD_PUT_SYSTEM);
+ }
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -834,16 +920,24 @@
* @return the corresponding value, or null if not present
*/
public synchronized static String getString(ContentResolver resolver, String name) {
+ return getStringForUser(resolver, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public synchronized static String getStringForUser(ContentResolver resolver, String name,
+ int userHandle) {
if (MOVED_TO_SECURE.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Secure, returning read-only value.");
- return Secure.getString(resolver, name);
+ return Secure.getStringForUser(resolver, name, userHandle);
}
- if (sNameValueCache == null) {
- sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
- CALL_METHOD_GET_SYSTEM);
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ + " to android.provider.Settings.Global, returning read-only value.");
+ return Global.getStringForUser(resolver, name, userHandle);
}
- return sNameValueCache.getString(resolver, name);
+ lazyInitCache();
+ return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
/**
@@ -854,12 +948,24 @@
* @return true if the value was set, false on database errors
*/
public static boolean putString(ContentResolver resolver, String name, String value) {
+ return putStringForUser(resolver, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putStringForUser(ContentResolver resolver, String name, String value,
+ int userHandle) {
if (MOVED_TO_SECURE.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Secure, value is unchanged.");
return false;
}
- return putString(resolver, CONTENT_URI, name, value);
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ + " to android.provider.Settings.Global, value is unchanged.");
+ return false;
+ }
+ lazyInitCache();
+ return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
}
/**
@@ -874,6 +980,11 @@
+ " to android.provider.Settings.Secure, returning Secure URI.");
return Secure.getUriFor(Secure.CONTENT_URI, name);
}
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ + " to android.provider.Settings.Global, returning read-only global URI.");
+ return Global.getUriFor(Global.CONTENT_URI, name);
+ }
return getUriFor(CONTENT_URI, name);
}
@@ -892,7 +1003,12 @@
* or not a valid integer.
*/
public static int getInt(ContentResolver cr, String name, int def) {
- String v = getString(cr, name);
+ return getIntForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ String v = getStringForUser(cr, name, userHandle);
try {
return v != null ? Integer.parseInt(v) : def;
} catch (NumberFormatException e) {
@@ -920,7 +1036,13 @@
*/
public static int getInt(ContentResolver cr, String name)
throws SettingNotFoundException {
- String v = getString(cr, name);
+ return getIntForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static int getIntForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String v = getStringForUser(cr, name, userHandle);
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
@@ -942,7 +1064,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putInt(ContentResolver cr, String name, int value) {
- return putString(cr, name, Integer.toString(value));
+ return putIntForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putIntForUser(ContentResolver cr, String name, int value,
+ int userHandle) {
+ return putStringForUser(cr, name, Integer.toString(value), userHandle);
}
/**
@@ -960,7 +1088,13 @@
* or not a valid {@code long}.
*/
public static long getLong(ContentResolver cr, String name, long def) {
- String valString = getString(cr, name);
+ return getLongForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static long getLongForUser(ContentResolver cr, String name, long def,
+ int userHandle) {
+ String valString = getStringForUser(cr, name, userHandle);
long value;
try {
value = valString != null ? Long.parseLong(valString) : def;
@@ -989,7 +1123,13 @@
*/
public static long getLong(ContentResolver cr, String name)
throws SettingNotFoundException {
- String valString = getString(cr, name);
+ return getLongForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static long getLongForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String valString = getStringForUser(cr, name, userHandle);
try {
return Long.parseLong(valString);
} catch (NumberFormatException e) {
@@ -1011,7 +1151,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putLong(ContentResolver cr, String name, long value) {
- return putString(cr, name, Long.toString(value));
+ return putLongForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putLongForUser(ContentResolver cr, String name, long value,
+ int userHandle) {
+ return putStringForUser(cr, name, Long.toString(value), userHandle);
}
/**
@@ -1029,7 +1175,13 @@
* or not a valid float.
*/
public static float getFloat(ContentResolver cr, String name, float def) {
- String v = getString(cr, name);
+ return getFloatForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static float getFloatForUser(ContentResolver cr, String name, float def,
+ int userHandle) {
+ String v = getStringForUser(cr, name, userHandle);
try {
return v != null ? Float.parseFloat(v) : def;
} catch (NumberFormatException e) {
@@ -1057,7 +1209,13 @@
*/
public static float getFloat(ContentResolver cr, String name)
throws SettingNotFoundException {
- String v = getString(cr, name);
+ return getFloatForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static float getFloatForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String v = getStringForUser(cr, name, userHandle);
if (v == null) {
throw new SettingNotFoundException(name);
}
@@ -1082,7 +1240,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putFloat(ContentResolver cr, String name, float value) {
- return putString(cr, name, Float.toString(value));
+ return putFloatForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putFloatForUser(ContentResolver cr, String name, float value,
+ int userHandle) {
+ return putStringForUser(cr, name, Float.toString(value), userHandle);
}
/**
@@ -1094,8 +1258,14 @@
* @param outConfig Where to place the configuration settings.
*/
public static void getConfiguration(ContentResolver cr, Configuration outConfig) {
- outConfig.fontScale = Settings.System.getFloat(
- cr, FONT_SCALE, outConfig.fontScale);
+ getConfigurationForUser(cr, outConfig, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static void getConfigurationForUser(ContentResolver cr, Configuration outConfig,
+ int userHandle) {
+ outConfig.fontScale = Settings.System.getFloatForUser(
+ cr, FONT_SCALE, outConfig.fontScale, userHandle);
if (outConfig.fontScale < 0) {
outConfig.fontScale = 1;
}
@@ -1118,7 +1288,13 @@
* @return true if the values were set, false on database errors
*/
public static boolean putConfiguration(ContentResolver cr, Configuration config) {
- return Settings.System.putFloat(cr, FONT_SCALE, config.fontScale);
+ return putConfigurationForUser(cr, config, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putConfigurationForUser(ContentResolver cr, Configuration config,
+ int userHandle) {
+ return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle);
}
/** @hide */
@@ -1126,12 +1302,35 @@
return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
}
+ /** @deprecated - Do not use */
+ @Deprecated
public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
- return getInt(cr, SHOW_GTALK_SERVICE_STATUS, 0) != 0;
+ return getShowGTalkServiceStatusForUser(cr, UserHandle.myUserId());
}
+ /**
+ * @hide
+ * @deprecated - Do not use
+ */
+ public static boolean getShowGTalkServiceStatusForUser(ContentResolver cr,
+ int userHandle) {
+ return getIntForUser(cr, SHOW_GTALK_SERVICE_STATUS, 0, userHandle) != 0;
+ }
+
+ /** @deprecated - Do not use */
+ @Deprecated
public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
- putInt(cr, SHOW_GTALK_SERVICE_STATUS, flag ? 1 : 0);
+ /* intentionally empty */
+ }
+
+ /**
+ * @hide
+ * @deprecated - Do not use
+ */
+ @Deprecated
+ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean flag,
+ int userHandle) {
+ putIntForUser(cr, SHOW_GTALK_SERVICE_STATUS, flag ? 1 : 0, userHandle);
}
/**
@@ -1141,17 +1340,10 @@
Uri.parse("content://" + AUTHORITY + "/system");
/**
- * Whether we keep the device on while the device is plugged in.
- * Supported values are:
- * <ul>
- * <li>{@code 0} to never stay on while plugged in</li>
- * <li>{@link BatteryManager#BATTERY_PLUGGED_AC} to stay on for AC charger</li>
- * <li>{@link BatteryManager#BATTERY_PLUGGED_USB} to stay on for USB charger</li>
- * <li>{@link BatteryManager#BATTERY_PLUGGED_WIRELESS} to stay on for wireless charger</li>
- * </ul>
- * These values can be OR-ed together.
+ * @deprecated Use {@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} instead
*/
- public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
+ @Deprecated
+ public static final String STAY_ON_WHILE_PLUGGED_IN = Global.STAY_ON_WHILE_PLUGGED_IN;
/**
* What happens when the user presses the end call button if they're not
@@ -1196,80 +1388,81 @@
public static final int ADVANCED_SETTINGS_DEFAULT = 0;
/**
- * Whether Airplane Mode is on.
+ * @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
*/
- public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
+ @Deprecated
+ public static final String AIRPLANE_MODE_ON = Global.AIRPLANE_MODE_ON;
/**
- * Constant for use in AIRPLANE_MODE_RADIOS to specify Bluetooth radio.
+ * @deprecated Use {@link android.provider.Settings.Global#RADIO_BLUETOOTH} instead
*/
- public static final String RADIO_BLUETOOTH = "bluetooth";
+ @Deprecated
+ public static final String RADIO_BLUETOOTH = Global.RADIO_BLUETOOTH;
/**
- * Constant for use in AIRPLANE_MODE_RADIOS to specify Wi-Fi radio.
+ * @deprecated Use {@link android.provider.Settings.Global#RADIO_WIFI} instead
*/
- public static final String RADIO_WIFI = "wifi";
+ @Deprecated
+ public static final String RADIO_WIFI = Global.RADIO_WIFI;
/**
+ * @deprecated Use {@link android.provider.Settings.Global#RADIO_WIMAX} instead
* {@hide}
*/
- public static final String RADIO_WIMAX = "wimax";
- /**
- * Constant for use in AIRPLANE_MODE_RADIOS to specify Cellular radio.
- */
- public static final String RADIO_CELL = "cell";
+ @Deprecated
+ public static final String RADIO_WIMAX = Global.RADIO_WIMAX;
/**
- * Constant for use in AIRPLANE_MODE_RADIOS to specify NFC radio.
+ * @deprecated Use {@link android.provider.Settings.Global#RADIO_CELL} instead
*/
- public static final String RADIO_NFC = "nfc";
+ @Deprecated
+ public static final String RADIO_CELL = Global.RADIO_CELL;
/**
- * A comma separated list of radios that need to be disabled when airplane mode
- * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are
- * included in the comma separated list.
+ * @deprecated Use {@link android.provider.Settings.Global#RADIO_NFC} instead
*/
- public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
+ @Deprecated
+ public static final String RADIO_NFC = Global.RADIO_NFC;
/**
- * A comma separated list of radios that should to be disabled when airplane mode
- * is on, but can be manually reenabled by the user. For example, if RADIO_WIFI is
- * added to both AIRPLANE_MODE_RADIOS and AIRPLANE_MODE_TOGGLEABLE_RADIOS, then Wifi
- * will be turned off when entering airplane mode, but the user will be able to reenable
- * Wifi in the Settings app.
+ * @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_RADIOS} instead
+ */
+ @Deprecated
+ public static final String AIRPLANE_MODE_RADIOS = Global.AIRPLANE_MODE_RADIOS;
+
+ /**
+ * @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_TOGGLEABLE_RADIOS} instead
*
* {@hide}
*/
- public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
+ @Deprecated
+ public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS =
+ Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS;
/**
- * The policy for deciding when Wi-Fi should go to sleep (which will in
- * turn switch to using the mobile data as an Internet connection).
- * <p>
- * Set to one of {@link #WIFI_SLEEP_POLICY_DEFAULT},
- * {@link #WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED}, or
- * {@link #WIFI_SLEEP_POLICY_NEVER}.
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_SLEEP_POLICY} instead
*/
- public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+ @Deprecated
+ public static final String WIFI_SLEEP_POLICY = Global.WIFI_SLEEP_POLICY;
/**
- * Value for {@link #WIFI_SLEEP_POLICY} to use the default Wi-Fi sleep
- * policy, which is to sleep shortly after the turning off
- * according to the {@link #STAY_ON_WHILE_PLUGGED_IN} setting.
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_SLEEP_POLICY_DEFAULT} instead
*/
- public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
+ @Deprecated
+ public static final int WIFI_SLEEP_POLICY_DEFAULT = Global.WIFI_SLEEP_POLICY_DEFAULT;
/**
- * Value for {@link #WIFI_SLEEP_POLICY} to use the default policy when
- * the device is on battery, and never go to sleep when the device is
- * plugged in.
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED} instead
*/
- public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
+ @Deprecated
+ public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED =
+ Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED;
/**
- * Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_SLEEP_POLICY_NEVER} instead
*/
- public static final int WIFI_SLEEP_POLICY_NEVER = 2;
+ @Deprecated
+ public static final int WIFI_SLEEP_POLICY_NEVER = Global.WIFI_SLEEP_POLICY_NEVER;
//TODO: deprecate static IP constants
/**
@@ -1673,16 +1866,18 @@
public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
/**
- * Value to specify if the user prefers the date, time and time zone
- * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ * @deprecated Use {@link android.provider.Settings.Global#AUTO_TIME}
+ * instead
*/
- public static final String AUTO_TIME = "auto_time";
+ @Deprecated
+ public static final String AUTO_TIME = Global.AUTO_TIME;
/**
- * Value to specify if the user prefers the time zone
- * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ * @deprecated Use {@link android.provider.Settings.Global#AUTO_TIME_ZONE}
+ * instead
*/
- public static final String AUTO_TIME_ZONE = "auto_time_zone";
+ @Deprecated
+ public static final String AUTO_TIME_ZONE = Global.AUTO_TIME_ZONE;
/**
* Display times as 12 or 24 hours
@@ -1883,16 +2078,20 @@
"window_orientation_listener_log";
/**
- * Whether to play a sound for low-battery alerts.
+ * @deprecated Use {@link android.provider.Settings.Global#POWER_SOUNDS_ENABLED}
+ * instead
* @hide
*/
- public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
+ @Deprecated
+ public static final String POWER_SOUNDS_ENABLED = Global.POWER_SOUNDS_ENABLED;
/**
- * Whether to play a sound for dock events.
+ * @deprecated Use {@link android.provider.Settings.Global#DOCK_SOUNDS_ENABLED}
+ * instead
* @hide
*/
- public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled";
+ @Deprecated
+ public static final String DOCK_SOUNDS_ENABLED = Global.DOCK_SOUNDS_ENABLED;
/**
* Whether to play sounds when the keyguard is shown and dismissed.
@@ -1907,46 +2106,60 @@
public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled";
/**
- * URI for the low battery sound file.
+ * @deprecated Use {@link android.provider.Settings.Global#LOW_BATTERY_SOUND}
+ * instead
* @hide
*/
- public static final String LOW_BATTERY_SOUND = "low_battery_sound";
+ @Deprecated
+ public static final String LOW_BATTERY_SOUND = Global.LOW_BATTERY_SOUND;
/**
- * URI for the desk dock "in" event sound.
+ * @deprecated Use {@link android.provider.Settings.Global#DESK_DOCK_SOUND}
+ * instead
* @hide
*/
- public static final String DESK_DOCK_SOUND = "desk_dock_sound";
+ @Deprecated
+ public static final String DESK_DOCK_SOUND = Global.DESK_DOCK_SOUND;
/**
- * URI for the desk dock "out" event sound.
+ * @deprecated Use {@link android.provider.Settings.Global#DESK_UNDOCK_SOUND}
+ * instead
* @hide
*/
- public static final String DESK_UNDOCK_SOUND = "desk_undock_sound";
+ @Deprecated
+ public static final String DESK_UNDOCK_SOUND = Global.DESK_UNDOCK_SOUND;
/**
- * URI for the car dock "in" event sound.
+ * @deprecated Use {@link android.provider.Settings.Global#CAR_DOCK_SOUND}
+ * instead
* @hide
*/
- public static final String CAR_DOCK_SOUND = "car_dock_sound";
+ @Deprecated
+ public static final String CAR_DOCK_SOUND = Global.CAR_DOCK_SOUND;
/**
- * URI for the car dock "out" event sound.
+ * @deprecated Use {@link android.provider.Settings.Global#CAR_UNDOCK_SOUND}
+ * instead
* @hide
*/
- public static final String CAR_UNDOCK_SOUND = "car_undock_sound";
+ @Deprecated
+ public static final String CAR_UNDOCK_SOUND = Global.CAR_UNDOCK_SOUND;
/**
- * URI for the "device locked" (keyguard shown) sound.
+ * @deprecated Use {@link android.provider.Settings.Global#LOCK_SOUND}
+ * instead
* @hide
*/
- public static final String LOCK_SOUND = "lock_sound";
+ @Deprecated
+ public static final String LOCK_SOUND = Global.LOCK_SOUND;
/**
- * URI for the "device unlocked" (keyguard dismissed) sound.
+ * @deprecated Use {@link android.provider.Settings.Global#UNLOCK_SOUND}
+ * instead
* @hide
*/
- public static final String UNLOCK_SOUND = "unlock_sound";
+ @Deprecated
+ public static final String UNLOCK_SOUND = Global.UNLOCK_SOUND;
/**
* Receive incoming SIP calls?
@@ -2066,11 +2279,11 @@
// Settings moved to Settings.Secure
/**
- * @deprecated Use {@link android.provider.Settings.Secure#ADB_ENABLED}
+ * @deprecated Use {@link android.provider.Settings.Global#ADB_ENABLED}
* instead
*/
@Deprecated
- public static final String ADB_ENABLED = Secure.ADB_ENABLED;
+ public static final String ADB_ENABLED = Global.ADB_ENABLED;
/**
* @deprecated Use {@link android.provider.Settings.Secure#ANDROID_ID} instead
@@ -2079,22 +2292,22 @@
public static final String ANDROID_ID = Secure.ANDROID_ID;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#BLUETOOTH_ON} instead
+ * @deprecated Use {@link android.provider.Settings.Global#BLUETOOTH_ON} instead
*/
@Deprecated
- public static final String BLUETOOTH_ON = Secure.BLUETOOTH_ON;
+ public static final String BLUETOOTH_ON = Global.BLUETOOTH_ON;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#DATA_ROAMING} instead
+ * @deprecated Use {@link android.provider.Settings.Global#DATA_ROAMING} instead
*/
@Deprecated
- public static final String DATA_ROAMING = Secure.DATA_ROAMING;
+ public static final String DATA_ROAMING = Global.DATA_ROAMING;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#DEVICE_PROVISIONED} instead
+ * @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
- public static final String DEVICE_PROVISIONED = Secure.DEVICE_PROVISIONED;
+ public static final String DEVICE_PROVISIONED = Global.DEVICE_PROVISIONED;
/**
* @deprecated Use {@link android.provider.Settings.Secure#HTTP_PROXY} instead
@@ -2103,10 +2316,10 @@
public static final String HTTP_PROXY = Secure.HTTP_PROXY;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead
+ * @deprecated Use {@link android.provider.Settings.Global#INSTALL_NON_MARKET_APPS} instead
*/
@Deprecated
- public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
+ public static final String INSTALL_NON_MARKET_APPS = Global.INSTALL_NON_MARKET_APPS;
/**
* @deprecated Use {@link android.provider.Settings.Secure#LOCATION_PROVIDERS_ALLOWED}
@@ -2122,10 +2335,10 @@
public static final String LOGGING_ID = Secure.LOGGING_ID;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#NETWORK_PREFERENCE} instead
+ * @deprecated Use {@link android.provider.Settings.Global#NETWORK_PREFERENCE} instead
*/
@Deprecated
- public static final String NETWORK_PREFERENCE = Secure.NETWORK_PREFERENCE;
+ public static final String NETWORK_PREFERENCE = Global.NETWORK_PREFERENCE;
/**
* @deprecated Use {@link android.provider.Settings.Secure#PARENTAL_CONTROL_ENABLED}
@@ -2156,60 +2369,60 @@
public static final String SETTINGS_CLASSNAME = Secure.SETTINGS_CLASSNAME;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#USB_MASS_STORAGE_ENABLED} instead
+ * @deprecated Use {@link android.provider.Settings.Global#USB_MASS_STORAGE_ENABLED} instead
*/
@Deprecated
- public static final String USB_MASS_STORAGE_ENABLED = Secure.USB_MASS_STORAGE_ENABLED;
+ public static final String USB_MASS_STORAGE_ENABLED = Global.USB_MASS_STORAGE_ENABLED;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#USE_GOOGLE_MAIL} instead
+ * @deprecated Use {@link android.provider.Settings.Global#USE_GOOGLE_MAIL} instead
*/
@Deprecated
- public static final String USE_GOOGLE_MAIL = Secure.USE_GOOGLE_MAIL;
+ public static final String USE_GOOGLE_MAIL = Global.USE_GOOGLE_MAIL;
/**
* @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT} instead
+ * {@link android.provider.Settings.Global#WIFI_MAX_DHCP_RETRY_COUNT} instead
*/
@Deprecated
- public static final String WIFI_MAX_DHCP_RETRY_COUNT = Secure.WIFI_MAX_DHCP_RETRY_COUNT;
+ public static final String WIFI_MAX_DHCP_RETRY_COUNT = Global.WIFI_MAX_DHCP_RETRY_COUNT;
/**
* @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS} instead
+ * {@link android.provider.Settings.Global#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS} instead
*/
@Deprecated
public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
- Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS;
+ Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS;
/**
* @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} instead
+ * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} instead
*/
@Deprecated
public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
- Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
+ Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
/**
* @deprecated Use
- * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} instead
+ * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} instead
*/
@Deprecated
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
- Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
+ Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_NUM_OPEN_NETWORKS_KEPT}
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
* instead
*/
@Deprecated
- public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Secure.WIFI_NUM_OPEN_NETWORKS_KEPT;
+ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_ON} instead
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead
*/
@Deprecated
- public static final String WIFI_ON = Secure.WIFI_ON;
+ public static final String WIFI_ON = Global.WIFI_ON;
/**
* @deprecated Use
@@ -2267,10 +2480,10 @@
public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = Secure.WIFI_WATCHDOG_MAX_AP_CHECKS;
/**
- * @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_ON} instead
+ * @deprecated Use {@link android.provider.Settings.Global#WIFI_WATCHDOG_ON} instead
*/
@Deprecated
- public static final String WIFI_WATCHDOG_ON = Secure.WIFI_WATCHDOG_ON;
+ public static final String WIFI_WATCHDOG_ON = Global.WIFI_WATCHDOG_ON;
/**
* @deprecated Use {@link android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT} instead
@@ -2310,11 +2523,106 @@
private static boolean sIsSystemProcess;
private static final HashSet<String> MOVED_TO_LOCK_SETTINGS;
+ private static final HashSet<String> MOVED_TO_GLOBAL;
static {
MOVED_TO_LOCK_SETTINGS = new HashSet<String>(3);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE);
MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
+
+ MOVED_TO_GLOBAL = new HashSet<String>();
+ MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.BLUETOOTH_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.CDMA_CELL_BROADCAST_SMS);
+ MOVED_TO_GLOBAL.add(Settings.Global.CDMA_ROAMING_MODE);
+ MOVED_TO_GLOBAL.add(Settings.Global.CDMA_SUBSCRIPTION_MODE);
+ MOVED_TO_GLOBAL.add(Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE);
+ MOVED_TO_GLOBAL.add(Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI);
+ MOVED_TO_GLOBAL.add(Settings.Global.DATA_ROAMING);
+ MOVED_TO_GLOBAL.add(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.DEVICE_PROVISIONED);
+ MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_DENSITY_FORCED);
+ MOVED_TO_GLOBAL.add(Settings.Global.DISPLAY_SIZE_FORCED);
+ MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
+ MOVED_TO_GLOBAL.add(Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+ MOVED_TO_GLOBAL.add(Settings.Global.INSTALL_NON_MARKET_APPS);
+ MOVED_TO_GLOBAL.add(Settings.Global.MOBILE_DATA);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_BUCKET_DURATION);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_DELETE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_PERSIST_BYTES);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_DEV_ROTATE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_POLL_INTERVAL);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_REPORT_XT_OVER_DEV);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_SAMPLE_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_BUCKET_DURATION);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_DELETE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_PERSIST_BYTES);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_ROTATE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_TAG_DELETE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NETWORK_PREFERENCE);
+ MOVED_TO_GLOBAL.add(Settings.Global.NITZ_UPDATE_DIFF);
+ MOVED_TO_GLOBAL.add(Settings.Global.NITZ_UPDATE_SPACING);
+ MOVED_TO_GLOBAL.add(Settings.Global.NTP_SERVER);
+ MOVED_TO_GLOBAL.add(Settings.Global.NTP_TIMEOUT);
+ MOVED_TO_GLOBAL.add(Settings.Global.PDP_WATCHDOG_ERROR_POLL_COUNT);
+ MOVED_TO_GLOBAL.add(Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT);
+ MOVED_TO_GLOBAL.add(Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT);
+ MOVED_TO_GLOBAL.add(Settings.Global.SAMPLING_PROFILER_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST);
+ MOVED_TO_GLOBAL.add(Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.TETHER_DUN_APN);
+ MOVED_TO_GLOBAL.add(Settings.Global.TETHER_DUN_REQUIRED);
+ MOVED_TO_GLOBAL.add(Settings.Global.TETHER_SUPPORTED);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_HELP_URI);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_NOTIFICATION_TYPE);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_POLLING_SEC);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_RESET_DAY);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_THRESHOLD_BYTES);
+ MOVED_TO_GLOBAL.add(Settings.Global.THROTTLE_VALUE_KBITSPS);
+ MOVED_TO_GLOBAL.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.USE_GOOGLE_MAIL);
+ MOVED_TO_GLOBAL.add(Settings.Global.WEB_AUTOFILL_QUERY_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_COUNTRY_CODE);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FREQUENCY_BAND);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_IDLE_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NUM_OPEN_NETWORKS_KEPT);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_P2P_DEVICE_NAME);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SAVED_STATE);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_NUM_ARP_PINGS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.WTF_IS_FATAL);
+ }
+
+ private static void lazyInitCache() {
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(
+ SYS_PROP_SETTING_VERSION + '_' + UserHandle.myUserId(),
+ CONTENT_URI,
+ CALL_METHOD_GET_SECURE,
+ CALL_METHOD_PUT_SECURE);
+ }
}
/**
@@ -2324,9 +2632,16 @@
* @return the corresponding value, or null if not present
*/
public synchronized static String getString(ContentResolver resolver, String name) {
- if (sNameValueCache == null) {
- sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
- CALL_METHOD_GET_SECURE);
+ return getStringForUser(resolver, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public synchronized static String getStringForUser(ContentResolver resolver, String name,
+ int userHandle) {
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
+ + " to android.provider.Settings.Global.");
+ return Global.getStringForUser(resolver, name, userHandle);
}
if (sLockSettings == null) {
@@ -2337,13 +2652,14 @@
if (sLockSettings != null && !sIsSystemProcess
&& MOVED_TO_LOCK_SETTINGS.contains(name)) {
try {
- return sLockSettings.getString(name, "0", UserHandle.getCallingUserId());
+ return sLockSettings.getString(name, "0", userHandle);
} catch (RemoteException re) {
// Fall through
}
}
- return sNameValueCache.getString(resolver, name);
+ lazyInitCache();
+ return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
/**
@@ -2353,9 +2669,20 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- public static boolean putString(ContentResolver resolver,
- String name, String value) {
- return putString(resolver, CONTENT_URI, name, value);
+ public static boolean putString(ContentResolver resolver, String name, String value) {
+ return putStringForUser(resolver, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putStringForUser(ContentResolver resolver, String name, String value,
+ int userHandle) {
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ + " to android.provider.Settings.Global");
+ return Global.putStringForUser(resolver, name, value, userHandle);
+ }
+ lazyInitCache();
+ return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
}
/**
@@ -2365,6 +2692,11 @@
* @return the corresponding content URI, or null if not present
*/
public static Uri getUriFor(String name) {
+ if (MOVED_TO_GLOBAL.contains(name)) {
+ Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
+ + " to android.provider.Settings.Global, returning global URI.");
+ return Global.getUriFor(Global.CONTENT_URI, name);
+ }
return getUriFor(CONTENT_URI, name);
}
@@ -2383,7 +2715,12 @@
* or not a valid integer.
*/
public static int getInt(ContentResolver cr, String name, int def) {
- String v = getString(cr, name);
+ return getIntForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ String v = getStringForUser(cr, name, userHandle);
try {
return v != null ? Integer.parseInt(v) : def;
} catch (NumberFormatException e) {
@@ -2411,7 +2748,13 @@
*/
public static int getInt(ContentResolver cr, String name)
throws SettingNotFoundException {
- String v = getString(cr, name);
+ return getIntForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static int getIntForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String v = getStringForUser(cr, name, userHandle);
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
@@ -2433,7 +2776,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putInt(ContentResolver cr, String name, int value) {
- return putString(cr, name, Integer.toString(value));
+ return putIntForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putIntForUser(ContentResolver cr, String name, int value,
+ int userHandle) {
+ return putStringForUser(cr, name, Integer.toString(value), userHandle);
}
/**
@@ -2451,7 +2800,13 @@
* or not a valid {@code long}.
*/
public static long getLong(ContentResolver cr, String name, long def) {
- String valString = getString(cr, name);
+ return getLongForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static long getLongForUser(ContentResolver cr, String name, long def,
+ int userHandle) {
+ String valString = getStringForUser(cr, name, userHandle);
long value;
try {
value = valString != null ? Long.parseLong(valString) : def;
@@ -2480,7 +2835,13 @@
*/
public static long getLong(ContentResolver cr, String name)
throws SettingNotFoundException {
- String valString = getString(cr, name);
+ return getLongForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static long getLongForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String valString = getStringForUser(cr, name, userHandle);
try {
return Long.parseLong(valString);
} catch (NumberFormatException e) {
@@ -2502,7 +2863,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putLong(ContentResolver cr, String name, long value) {
- return putString(cr, name, Long.toString(value));
+ return putLongForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putLongForUser(ContentResolver cr, String name, long value,
+ int userHandle) {
+ return putStringForUser(cr, name, Long.toString(value), userHandle);
}
/**
@@ -2520,7 +2887,13 @@
* or not a valid float.
*/
public static float getFloat(ContentResolver cr, String name, float def) {
- String v = getString(cr, name);
+ return getFloatForUser(cr, name, def, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static float getFloatForUser(ContentResolver cr, String name, float def,
+ int userHandle) {
+ String v = getStringForUser(cr, name, userHandle);
try {
return v != null ? Float.parseFloat(v) : def;
} catch (NumberFormatException e) {
@@ -2548,7 +2921,13 @@
*/
public static float getFloat(ContentResolver cr, String name)
throws SettingNotFoundException {
- String v = getString(cr, name);
+ return getFloatForUser(cr, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static float getFloatForUser(ContentResolver cr, String name, int userHandle)
+ throws SettingNotFoundException {
+ String v = getStringForUser(cr, name, userHandle);
if (v == null) {
throw new SettingNotFoundException(name);
}
@@ -2573,7 +2952,13 @@
* @return true if the value was set, false on database errors
*/
public static boolean putFloat(ContentResolver cr, String name, float value) {
- return putString(cr, name, Float.toString(value));
+ return putFloatForUser(cr, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putFloatForUser(ContentResolver cr, String name, float value,
+ int userHandle) {
+ return putStringForUser(cr, name, Float.toString(value), userHandle);
}
/**
@@ -2583,9 +2968,12 @@
Uri.parse("content://" + AUTHORITY + "/secure");
/**
- * Whether user has enabled development settings.
+ * @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED}
+ * instead
*/
- public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ @Deprecated
+ public static final String DEVELOPMENT_SETTINGS_ENABLED =
+ Global.DEVELOPMENT_SETTINGS_ENABLED;
/**
* When the user has enable the option to have a "bug report" command
@@ -2595,9 +2983,10 @@
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
- * Whether ADB is enabled.
+ * @deprecated Use {@link android.provider.Settings.Global#ADB_ENABLED} instead
*/
- public static final String ADB_ENABLED = "adb_enabled";
+ @Deprecated
+ public static final String ADB_ENABLED = Global.ADB_ENABLED;
/**
* Setting to allow mock locations and location provider status to be injected into the
@@ -2616,10 +3005,10 @@
public static final String ANDROID_ID = "android_id";
/**
- * Whether bluetooth is enabled/disabled
- * 0=disabled. 1=enabled.
+ * @deprecated Use {@link android.provider.Settings.Global#BLUETOOTH_ON} instead
*/
- public static final String BLUETOOTH_ON = "bluetooth_on";
+ @Deprecated
+ public static final String BLUETOOTH_ON = Global.BLUETOOTH_ON;
/**
* Get the key that retrieves a bluetooth headset's priority.
@@ -2646,9 +3035,9 @@
}
/**
- * Whether or not data roaming is enabled. (0 = false, 1 = true)
+ * @deprecated Use {@link android.provider.Settings.Global#DATA_ROAMING} instead
*/
- public static final String DATA_ROAMING = "data_roaming";
+ public static final String DATA_ROAMING = Global.DATA_ROAMING;
/**
* Setting to record the input method used by default, holding the ID
@@ -2678,9 +3067,10 @@
"input_method_selector_visibility";
/**
- * Whether the device has been provisioned (0 = false, 1 = true)
+ * @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
- public static final String DEVICE_PROVISIONED = "device_provisioned";
+ @Deprecated
+ public static final String DEVICE_PROVISIONED = Global.DEVICE_PROVISIONED;
/**
* List of input methods that are currently enabled. This is a string
@@ -2739,13 +3129,10 @@
public static final String DEFAULT_DNS_SERVER = "default_dns_server";
/**
- * Whether the package installer should allow installation of apps downloaded from
- * sources other than Google Play.
- *
- * 1 = allow installing from other sources
- * 0 = only allow installing from Google Play
+ * @deprecated Use {@link android.provider.Settings.Global#INSTALL_NON_MARKET_APPS} instead
*/
- public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+ @Deprecated
+ public static final String INSTALL_NON_MARKET_APPS = Global.INSTALL_NON_MARKET_APPS;
/**
* Comma-separated list of location providers that activities may access.
@@ -2797,24 +3184,25 @@
"lock_screen_owner_info_enabled";
/**
- * The saved value for WindowManagerService.setForcedDisplaySize().
- * Two integers separated by a comma. If unset, then use the real display size.
+ * @deprecated Use {@link android.provider.Settings.Global#DISPLAY_SIZE_FORCED} instead
* @hide
*/
- public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
+ @Deprecated
+ public static final String DISPLAY_SIZE_FORCED = Global.DISPLAY_SIZE_FORCED;
/**
- * The saved value for WindowManagerService.setForcedDisplayDensity().
- * One integer in dpi. If unset, then use the real display density.
+ * @deprecated Use {@link android.provider.Settings.Global#DISPLAY_DENSITY_FORCED} instead
* @hide
*/
- public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
+ @Deprecated
+ public static final String DISPLAY_DENSITY_FORCED = Global.DISPLAY_DENSITY_FORCED;
/**
- * Whether assisted GPS should be enabled or not.
+ * @deprecated Use {@link android.provider.Settings.Global#ASSISTED_GPS_ENABLE} instead
* @hide
*/
- public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+ @Deprecated
+ public static final String ASSISTED_GPS_ENABLED = Global.ASSISTED_GPS_ENABLED;
/**
* The Logging ID (a unique 64-bit value) as a hex string.
@@ -2826,57 +3214,48 @@
public static final String LOGGING_ID = "logging_id";
/**
- * User preference for which network(s) should be used. Only the
- * connectivity service should touch this.
+ * @deprecated Use {@link android.provider.Settings.Global#NETWORK_PREFERENCE} instead
*/
- public static final String NETWORK_PREFERENCE = "network_preference";
+ @Deprecated
+ public static final String NETWORK_PREFERENCE = Global.NETWORK_PREFERENCE;
/**
- * Used to disable Tethering on a device - defaults to true
+ * @deprecated Use {@link android.provider.Settings.Global#TETHER_SUPPORTED} instead
* @hide
*/
- public static final String TETHER_SUPPORTED = "tether_supported";
+ @Deprecated
+ public static final String TETHER_SUPPORTED = Global.TETHER_SUPPORTED;
/**
- * Used to require DUN APN on the device or not - defaults to a build config value
- * which defaults to false
+ * @deprecated Use {@link android.provider.Settings.Global#TETHER_DUN_REQUIRED} instead
* @hide
*/
- public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
+ @Deprecated
+ public static final String TETHER_DUN_REQUIRED = Global.TETHER_DUN_REQUIRED;
/**
- * Used to hold a gservices-provisioned apn value for DUN. If set, or the
- * corresponding build config values are set it will override the APN DB
- * values.
- * Consists of a comma seperated list of strings:
- * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
- * note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN"
+ * @deprecated Use {@link android.provider.Settings.Global#TETHER_DUN_REQUIRED} instead
* @hide
*/
- public static final String TETHER_DUN_APN = "tether_dun_apn";
+ @Deprecated
+ public static final String TETHER_DUN_APN = Global.TETHER_DUN_APN;
- /** Inactivity timeout to track mobile data activity.
- *
- * If set to a positive integer, it indicates the inactivity timeout value in seconds to
- * infer the data activity of mobile network. After a period of no activity on mobile
- * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
- * intent is fired to indicate a transition of network status from "active" to "idle". Any
- * subsequent activity on mobile networks triggers the firing of {@code
- * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
- *
- * Network activity refers to transmitting or receiving data on the network interfaces.
- *
- * Tracking is disabled if set to zero or negative value.
- *
+ /**
+ * @deprecated Use {@link android.provider.Settings.Global#DATA_ACTIVITY_TIMEOUT_MOBILE}
+ * instead
* @hide
*/
- public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
+ @Deprecated
+ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE =
+ Global.DATA_ACTIVITY_TIMEOUT_MOBILE;
- /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
- * but for Wifi network.
+ /**
+ * @deprecated Use {@link android.provider.Settings.Global#DATA_ACTIVITY_TIMEOUT_MOBILE}
+ * instead
* @hide
*/
- public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
+ @Deprecated
+ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = Global.DATA_ACTIVITY_TIMEOUT_WIFI;
/**
* No longer supported.
@@ -2894,13 +3273,11 @@
public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
/**
- * A positive value indicates how often the SamplingProfiler
- * should take snapshots. Zero value means SamplingProfiler
- * is disabled.
- *
+ * @deprecated Use {@link android.provider.Settings.Global#SAMPLING_PROFILER_MS} instead
* @hide
*/
- public static final String SAMPLING_PROFILER_MS = "sampling_profiler_ms";
+ @Deprecated
+ public static final String SAMPLING_PROFILER_MS = Global.SAMPLING_PROFILER_MS;
/**
* Settings classname to launch when Settings is clicked from All
@@ -2911,15 +3288,16 @@
public static final String SETTINGS_CLASSNAME = "settings_classname";
/**
- * USB Mass Storage Enabled
+ * @deprecated Use {@link android.provider.Settings.Global#USB_MASS_STORAGE_ENABLED} instead
*/
- public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+ @Deprecated
+ public static final String USB_MASS_STORAGE_ENABLED = Global.USB_MASS_STORAGE_ENABLED;
/**
- * If this setting is set (to anything), then all references
- * to Gmail on the device must change to Google Mail.
+ * @deprecated Use {@link android.provider.Settings.Global#USE_GOOGLE_MAIL} instead
*/
- public static final String USE_GOOGLE_MAIL = "use_google_mail";
+ @Deprecated
+ public static final String USE_GOOGLE_MAIL = Global.USE_GOOGLE_MAIL;
/**
* If accessibility is enabled.
@@ -3022,6 +3400,46 @@
"accessibility_web_content_key_bindings";
/**
+ * Setting that specifies whether the display magnification is enabled.
+ * Display magnifications allows the user to zoom in the display content
+ * and is targeted to low vision users. The current magnification scale
+ * is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
+ "accessibility_display_magnification_enabled";
+
+ /**
+ * Setting that specifies what the display magnification scale is.
+ * Display magnifications allows the user to zoom in the display
+ * content and is targeted to low vision users. Whether a display
+ * magnification is performed is controlled by
+ * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED}
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
+ "accessibility_display_magnification_scale";
+
+ /**
+ * Setting that specifies whether the display magnification should be
+ * automatically updated. If this fearture is enabled the system will
+ * exit magnification mode or pan the viewport when a context change
+ * occurs. For example, on staring a new activity or rotating the screen,
+ * the system may zoom out so the user can see the new context he is in.
+ * Another example is on showing a window that is not visible in the
+ * magnified viewport the system may pan the viewport to make the window
+ * the has popped up so the user knows that the context has changed.
+ * Whether a screen magnification is performed is controlled by
+ * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED}
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE =
+ "accessibility_display_magnification_auto_update";
+
+ /**
* The timout for considering a press to be a long press in milliseconds.
* @hide
*/
@@ -3279,13 +3697,6 @@
/**
- * ms delay before rechecking a connect SSID for walled garden with a http download.
- * @hide
- */
- public static final String WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS =
- "wifi_watchdog_walled_garden_interval_ms";
-
- /**
* Number of ARP pings per check.
* @hide
*/
@@ -3322,28 +3733,11 @@
"wifi_suspend_optimizations_enabled";
/**
- * Setting to turn off walled garden test on Wi-Fi. Feature is enabled by default and
- * the setting needs to be set to 0 to disable it.
- * @hide
+ * @deprecated Use
+ * {@link android.provider.Settings.Global#WIFI_MAX_DHCP_RETRY_COUNT} instead
*/
- public static final String WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED =
- "wifi_watchdog_walled_garden_test_enabled";
-
- /**
- * The URL used for walled garden check upon a new conection. WifiWatchdogService
- * fetches the URL and checks to see if {@link #WIFI_WATCHDOG_WALLED_GARDEN_PATTERN}
- * is not part of the title string to notify the user on the presence of a walled garden.
- * @hide
- */
- public static final String WIFI_WATCHDOG_WALLED_GARDEN_URL =
- "wifi_watchdog_walled_garden_url";
-
- /**
- * The maximum number of times we will retry a connection to an access
- * point for which we have failed in acquiring an IP address from DHCP.
- * A value of N means that we will make N+1 connection attempts in all.
- */
- public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+ @Deprecated
+ public static final String WIFI_MAX_DHCP_RETRY_COUNT = Global.WIFI_MAX_DHCP_RETRY_COUNT;
/**
* The operational wifi frequency band
@@ -3362,6 +3756,21 @@
public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
/**
+ * Setting to turn off captive portal detection. Feature is enabled by default and
+ * the setting needs to be set to 0 to disable it.
+ * @hide
+ */
+ public static final String CAPTIVE_PORTAL_DETECTION_ENABLED =
+ "captive_portal_detection_enabled";
+
+ /**
+ * The server used for captive portal detection upon a new conection. A 204 response
+ * code from the server is used for validation.
+ * @hide
+ */
+ public static final String CAPTIVE_PORTAL_SERVER = "captive_portal_server";
+
+ /**
* Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile
* data connectivity to be established after a disconnect from Wi-Fi.
*/
@@ -3389,26 +3798,25 @@
= "allowed_geolocation_origins";
/**
- * Whether mobile data connections are allowed by the user. See
- * ConnectivityManager for more info.
+ * @deprecated Use {@link android.provider.Settings.Global#MOBILE_DATA} instead
* @hide
*/
- public static final String MOBILE_DATA = "mobile_data";
+ @Deprecated
+ public static final String MOBILE_DATA = Global.MOBILE_DATA;
/**
- * The CDMA roaming mode 0 = Home Networks, CDMA default
- * 1 = Roaming on Affiliated networks
- * 2 = Roaming on any networks
+ * @deprecated Use {@link android.provider.Settings.Global#CDMA_ROAMING_MODE} instead
* @hide
*/
- public static final String CDMA_ROAMING_MODE = "roaming_settings";
+ @Deprecated
+ public static final String CDMA_ROAMING_MODE = Global.CDMA_ROAMING_MODE;
/**
- * The CDMA subscription mode 0 = RUIM/SIM (default)
- * 1 = NV
+ * @deprecated Use {@link android.provider.Settings.Global#CDMA_ROAMING_MODE} instead
* @hide
*/
- public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
+ @Deprecated
+ public static final String CDMA_SUBSCRIPTION_MODE = Global.CDMA_SUBSCRIPTION_MODE;
/**
* The preferred network mode 7 = Global
@@ -3436,13 +3844,11 @@
/**
- * CDMA Cell Broadcast SMS
- * 0 = CDMA Cell Broadcast SMS disabled
- * 1 = CDMA Cell Broadcast SMS enabled
+ * @deprecated Use {@link android.provider.Settings.Global#CDMA_CELL_BROADCAST_SMS} instead
* @hide
*/
- public static final String CDMA_CELL_BROADCAST_SMS =
- "cdma_cell_broadcast_sms";
+ @Deprecated
+ public static final String CDMA_CELL_BROADCAST_SMS = Global.CDMA_CELL_BROADCAST_SMS;
/**
* The cdma subscription 0 = Subscription from RUIM, when available
@@ -3662,10 +4068,11 @@
public static final String SEND_ACTION_APP_ERROR = "send_action_app_error";
/**
- * Nonzero causes Log.wtf() to crash.
+ * @deprecated Use {@link android.provider.Settings.Global#WTF_IS_FATAL} instead
* @hide
*/
- public static final String WTF_IS_FATAL = "wtf_is_fatal";
+ @Deprecated
+ public static final String WTF_IS_FATAL = Global.WTF_IS_FATAL;
/**
* Maximum age of entries kept by {@link com.android.internal.os.IDropBoxManagerService}.
@@ -3795,57 +4202,52 @@
"wifi_supplicant_scan_interval_ms";
/**
- * The interval in milliseconds at which to check packet counts on the
- * mobile data interface when screen is on, to detect possible data
- * connection problems.
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
- "pdp_watchdog_poll_interval_ms";
+ Global.PDP_WATCHDOG_POLL_INTERVAL_MS;
/**
- * The interval in milliseconds at which to check packet counts on the
- * mobile data interface when screen is off, to detect possible data
- * connection problems.
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
- "pdp_watchdog_long_poll_interval_ms";
+ Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS;
/**
- * The interval in milliseconds at which to check packet counts on the
- * mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT}
- * outgoing packets has been reached without incoming packets.
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
- "pdp_watchdog_error_poll_interval_ms";
+ Global.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS;
/**
- * The number of outgoing packets sent without seeing an incoming packet
- * that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT}
- * device is logged to the event log
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
- "pdp_watchdog_trigger_packet_count";
+ Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT;
/**
- * The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS})
- * after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before
- * attempting data connection recovery.
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
- "pdp_watchdog_error_poll_count";
+ Global.PDP_WATCHDOG_ERROR_POLL_COUNT;
/**
- * The number of failed PDP reset attempts before moving to something more
- * drastic: re-registering to the network.
+ * @deprecated Moved to Settings.Global
* @hide
*/
+ @Deprecated
public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
- "pdp_watchdog_max_pdp_reset_fail_count";
+ Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT;
/**
* The number of milliseconds to delay when checking for data stalls during
@@ -3874,19 +4276,16 @@
"gprs_register_check_period_ms";
/**
- * The length of time in milli-seconds that automatic small adjustments to
- * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
+ * @deprecated Use {@link android.provider.Settings.Global#NITZ_UPDATE_SPACING} instead
* @hide
*/
- public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
+ public static final String NITZ_UPDATE_SPACING = Global.NITZ_UPDATE_SPACING;
/**
- * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
- * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
- * exceeded.
+ * @deprecated Use {@link android.provider.Settings.Global#NITZ_UPDATE_SPACING} instead
* @hide
*/
- public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
+ public static final String NITZ_UPDATE_DIFF = Global.NITZ_UPDATE_DIFF;
/**
* The maximum reconnect delay for short network outages or when the network is suspended
@@ -4148,65 +4547,70 @@
public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
/**
- * The bandwidth throttle polling freqency in seconds
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_POLLING_SEC} instead
* @hide
*/
- public static final String THROTTLE_POLLING_SEC = "throttle_polling_sec";
+ @Deprecated
+ public static final String THROTTLE_POLLING_SEC = Global.THROTTLE_POLLING_SEC;
/**
- * The bandwidth throttle threshold (long)
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_THRESHOLD_BYTES} instead
* @hide
*/
- public static final String THROTTLE_THRESHOLD_BYTES = "throttle_threshold_bytes";
+ @Deprecated
+ public static final String THROTTLE_THRESHOLD_BYTES = Global.THROTTLE_THRESHOLD_BYTES;
/**
- * The bandwidth throttle value (kbps)
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_VALUE_KBITSPS} instead
* @hide
*/
- public static final String THROTTLE_VALUE_KBITSPS = "throttle_value_kbitsps";
+ @Deprecated
+ public static final String THROTTLE_VALUE_KBITSPS = Global.THROTTLE_VALUE_KBITSPS;
/**
- * The bandwidth throttle reset calendar day (1-28)
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_VALUE_KBITSPS} instead
* @hide
*/
- public static final String THROTTLE_RESET_DAY = "throttle_reset_day";
+ @Deprecated
+ public static final String THROTTLE_RESET_DAY = Global.THROTTLE_RESET_DAY;
/**
- * The throttling notifications we should send
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_NOTIFICATION_TYPE} instead
* @hide
*/
- public static final String THROTTLE_NOTIFICATION_TYPE = "throttle_notification_type";
+ @Deprecated
+ public static final String THROTTLE_NOTIFICATION_TYPE = Global.THROTTLE_NOTIFICATION_TYPE;
/**
- * Help URI for data throttling policy
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_HELP_URI} instead
* @hide
*/
- public static final String THROTTLE_HELP_URI = "throttle_help_uri";
+ @Deprecated
+ public static final String THROTTLE_HELP_URI = Global.THROTTLE_HELP_URI;
/**
- * The length of time in Sec that we allow our notion of NTP time
- * to be cached before we refresh it
+ * @deprecated Use {@link android.provider.Settings.Global#THROTTLE_MAX_NTP_CACHE_AGE_SEC} instead
* @hide
*/
+ @Deprecated
public static final String THROTTLE_MAX_NTP_CACHE_AGE_SEC =
- "throttle_max_ntp_cache_age_sec";
+ Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC;
/**
- * The maximum size, in bytes, of a download that the download manager will transfer over
- * a non-wifi connection.
+ * @deprecated Use {@link android.provider.Settings.Global#DOWNLOAD_MAX_BYTES_OVER_MOBILE} instead
* @hide
*/
+ @Deprecated
public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
- "download_manager_max_bytes_over_mobile";
+ Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE;
/**
- * The recommended maximum size, in bytes, of a download that the download manager should
- * transfer over a non-wifi connection. Over this size, the use will be warned, but will
- * have the option to start the download over the mobile connection anyway.
+ * @deprecated Use {@link android.provider.Settings.Global#DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE} instead
* @hide
*/
+ @Deprecated
public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
- "download_manager_recommended_max_bytes_over_mobile";
+ Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE;
/**
* ms during which to consume extra events related to Inet connection condition
@@ -4225,27 +4629,28 @@
"inet_condition_debounce_down_delay";
/**
- * URL to open browser on to allow user to manage a prepay account
+ * @deprecated Use {@link android.provider.Settings.Global#SETUP_PREPAID_DATA_SERVICE_URL} instead
* @hide
*/
+ @Deprecated
public static final String SETUP_PREPAID_DATA_SERVICE_URL =
- "setup_prepaid_data_service_url";
+ Global.SETUP_PREPAID_DATA_SERVICE_URL;
/**
- * URL to attempt a GET on to see if this is a prepay device
+ * @deprecated Use {@link android.provider.Settings.Global#SETUP_PREPAID_DETECTION_TARGET_URL} instead
* @hide
*/
+ @Deprecated
public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
- "setup_prepaid_detection_target_url";
+ Global.SETUP_PREPAID_DETECTION_TARGET_URL;
/**
- * Host to check for a redirect to after an attempt to GET
- * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
- * this is a prepaid device with zero balance.)
+ * @deprecated Use {@link android.provider.Settings.Global#SETUP_PREPAID_DETECTION_REDIR_HOST} instead
* @hide
*/
+ @Deprecated
public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
- "setup_prepaid_detection_redir_host";
+ Global.SETUP_PREPAID_DETECTION_REDIR_HOST;
/**
* Whether screensavers are enabled.
@@ -4282,57 +4687,104 @@
*/
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
- /** {@hide} */
- public static final String NETSTATS_ENABLED = "netstats_enabled";
- /** {@hide} */
- public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
- /** {@hide} */
- public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
- /** {@hide} */
- public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
- /** {@hide} */
- public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
- /** {@hide} */
- public static final String NETSTATS_REPORT_XT_OVER_DEV = "netstats_report_xt_over_dev";
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_ENABLED = Global.NETSTATS_ENABLED;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_POLL_INTERVAL = Global.NETSTATS_POLL_INTERVAL;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_TIME_CACHE_MAX_AGE = Global.NETSTATS_TIME_CACHE_MAX_AGE;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_GLOBAL_ALERT_BYTES = Global.NETSTATS_GLOBAL_ALERT_BYTES;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_SAMPLE_ENABLED = Global.NETSTATS_SAMPLE_ENABLED;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_REPORT_XT_OVER_DEV = Global.NETSTATS_REPORT_XT_OVER_DEV;
- /** {@hide} */
- public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_DEV_BUCKET_DURATION = Global.NETSTATS_DEV_BUCKET_DURATION;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_DEV_PERSIST_BYTES = Global.NETSTATS_DEV_PERSIST_BYTES;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_DEV_ROTATE_AGE = Global.NETSTATS_DEV_ROTATE_AGE;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_DEV_DELETE_AGE = Global.NETSTATS_DEV_DELETE_AGE;
- /** {@hide} */
- public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_BUCKET_DURATION = Global.NETSTATS_UID_BUCKET_DURATION;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_PERSIST_BYTES = Global.NETSTATS_UID_PERSIST_BYTES;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_ROTATE_AGE = Global.NETSTATS_UID_ROTATE_AGE;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_DELETE_AGE = Global.NETSTATS_UID_DELETE_AGE;
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_TAG_BUCKET_DURATION = Global.NETSTATS_UID_TAG_BUCKET_DURATION;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_TAG_PERSIST_BYTES = Global.NETSTATS_UID_TAG_PERSIST_BYTES;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_TAG_ROTATE_AGE = Global.NETSTATS_UID_TAG_ROTATE_AGE;
+ /** @deprecated The NETSTATS_* symbols live in Settings.Global.* now
+ * {@hide} */
+ @Deprecated
+ public static final String NETSTATS_UID_TAG_DELETE_AGE = Global.NETSTATS_UID_TAG_DELETE_AGE;
- /** Preferred NTP server. {@hide} */
- public static final String NTP_SERVER = "ntp_server";
- /** Timeout in milliseconds to wait for NTP server. {@hide} */
- public static final String NTP_TIMEOUT = "ntp_timeout";
+ /** Preferred NTP server. {@hide}
+ * @deprecated moved to Settings.Global */
+ public static final String NTP_SERVER = Global.NTP_SERVER;
- /** Autofill server address (Used in WebView/browser). {@hide} */
- public static final String WEB_AUTOFILL_QUERY_URL =
- "web_autofill_query_url";
+ /** Timeout in milliseconds to wait for NTP server. {@hide}
+ * @deprecated moved to Settings.Global */
+ public static final String NTP_TIMEOUT = Global.NTP_TIMEOUT;
- /** Whether package verification is enabled. {@hide} */
- public static final String PACKAGE_VERIFIER_ENABLE = "verifier_enable";
+ /** Autofill server address (Used in WebView/browser).
+ * @deprecated moved to Settings.Global
+ * {@hide} */
+ public static final String WEB_AUTOFILL_QUERY_URL = Global.WEB_AUTOFILL_QUERY_URL;
+
+ /**
+ * Whether the package manager should send package verification broadcasts for verifiers to
+ * review apps prior to installation.
+ *
+ * 1 = request apps to be verified prior to installation, if a verifier exists.
+ * 0 = do not verify apps before installation
+ * {@hide}
+ */
+ public static final String PACKAGE_VERIFIER_ENABLE = "package_verifier_enable";
/** Timeout for package verification. {@hide} */
public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
@@ -4360,6 +4812,25 @@
public static final String SMS_SHORT_CODES_PREFIX = "sms_short_codes_";
/**
+ * Overlay display devices setting.
+ * The associated value is a specially formatted string that describes the
+ * size and density of simulated secondary display devices.
+ * <p>
+ * Format: {width}x{height}/{dpi};...
+ * </p><p>
+ * Example:
+ * <ul>
+ * <li><code>1280x720/213</code>: make one overlay that is 1280x720 at 213dpi.</li>
+ * <li><code>1920x1080/320;1280x720/213</code>: make two overlays, the first
+ * at 1080p and the second at 720p.</li>
+ * <li>If the value is empty, then no overlay display devices are created.</li>
+ * </ul></p>
+ *
+ * @hide
+ */
+ public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4375,6 +4846,9 @@
PARENTAL_CONTROL_ENABLED,
PARENTAL_CONTROL_REDIRECT_URL,
USB_MASS_STORAGE_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
ACCESSIBILITY_SCRIPT_INJECTION,
BACKUP_AUTO_RESTORE,
ENABLED_ACCESSIBILITY_SERVICES,
@@ -4434,6 +4908,955 @@
}
/**
+ * Global system settings, containing preferences that always apply identically
+ * to all defined users. Applications can read these but are not allowed to write;
+ * like the "Secure" settings, these are for preferences that the user must
+ * explicitly modify through the system UI or specialized APIs for those values.
+ */
+ public static final class Global extends NameValueTable {
+ public static final String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
+
+ /**
+ * The content:// style URL for global secure settings items. Not public.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");
+
+ /**
+ * Whether Airplane Mode is on.
+ */
+ public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
+
+ /**
+ * Constant for use in AIRPLANE_MODE_RADIOS to specify Bluetooth radio.
+ */
+ public static final String RADIO_BLUETOOTH = "bluetooth";
+
+ /**
+ * Constant for use in AIRPLANE_MODE_RADIOS to specify Wi-Fi radio.
+ */
+ public static final String RADIO_WIFI = "wifi";
+
+ /**
+ * {@hide}
+ */
+ public static final String RADIO_WIMAX = "wimax";
+ /**
+ * Constant for use in AIRPLANE_MODE_RADIOS to specify Cellular radio.
+ */
+ public static final String RADIO_CELL = "cell";
+
+ /**
+ * Constant for use in AIRPLANE_MODE_RADIOS to specify NFC radio.
+ */
+ public static final String RADIO_NFC = "nfc";
+
+ /**
+ * A comma separated list of radios that need to be disabled when airplane mode
+ * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are
+ * included in the comma separated list.
+ */
+ public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
+
+ /**
+ * A comma separated list of radios that should to be disabled when airplane mode
+ * is on, but can be manually reenabled by the user. For example, if RADIO_WIFI is
+ * added to both AIRPLANE_MODE_RADIOS and AIRPLANE_MODE_TOGGLEABLE_RADIOS, then Wifi
+ * will be turned off when entering airplane mode, but the user will be able to reenable
+ * Wifi in the Settings app.
+ *
+ * {@hide}
+ */
+ public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
+
+ /**
+ * The policy for deciding when Wi-Fi should go to sleep (which will in
+ * turn switch to using the mobile data as an Internet connection).
+ * <p>
+ * Set to one of {@link #WIFI_SLEEP_POLICY_DEFAULT},
+ * {@link #WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED}, or
+ * {@link #WIFI_SLEEP_POLICY_NEVER}.
+ */
+ public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+
+ /**
+ * Value for {@link #WIFI_SLEEP_POLICY} to use the default Wi-Fi sleep
+ * policy, which is to sleep shortly after the turning off
+ * according to the {@link #STAY_ON_WHILE_PLUGGED_IN} setting.
+ */
+ public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
+
+ /**
+ * Value for {@link #WIFI_SLEEP_POLICY} to use the default policy when
+ * the device is on battery, and never go to sleep when the device is
+ * plugged in.
+ */
+ public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
+
+ /**
+ * Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
+ */
+ public static final int WIFI_SLEEP_POLICY_NEVER = 2;
+
+ /**
+ * Value to specify if the user prefers the date, time and time zone
+ * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ */
+ public static final String AUTO_TIME = "auto_time";
+
+ /**
+ * Value to specify if the user prefers the time zone
+ * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+ */
+ public static final String AUTO_TIME_ZONE = "auto_time_zone";
+
+ /**
+ * URI for the car dock "in" event sound.
+ * @hide
+ */
+ public static final String CAR_DOCK_SOUND = "car_dock_sound";
+
+ /**
+ * URI for the car dock "out" event sound.
+ * @hide
+ */
+ public static final String CAR_UNDOCK_SOUND = "car_undock_sound";
+
+ /**
+ * URI for the desk dock "in" event sound.
+ * @hide
+ */
+ public static final String DESK_DOCK_SOUND = "desk_dock_sound";
+
+ /**
+ * URI for the desk dock "out" event sound.
+ * @hide
+ */
+ public static final String DESK_UNDOCK_SOUND = "desk_undock_sound";
+
+ /**
+ * Whether to play a sound for dock events.
+ * @hide
+ */
+ public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled";
+
+ /**
+ * URI for the "device locked" (keyguard shown) sound.
+ * @hide
+ */
+ public static final String LOCK_SOUND = "lock_sound";
+
+ /**
+ * URI for the "device unlocked" sound.
+ * @hide
+ */
+ public static final String UNLOCK_SOUND = "unlock_sound";
+
+ /**
+ * URI for the low battery sound file.
+ * @hide
+ */
+ public static final String LOW_BATTERY_SOUND = "low_battery_sound";
+
+ /**
+ * Whether to play a sound for low-battery alerts.
+ * @hide
+ */
+ public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
+
+ /**
+ * Whether we keep the device on while the device is plugged in.
+ * Supported values are:
+ * <ul>
+ * <li>{@code 0} to never stay on while plugged in</li>
+ * <li>{@link BatteryManager#BATTERY_PLUGGED_AC} to stay on for AC charger</li>
+ * <li>{@link BatteryManager#BATTERY_PLUGGED_USB} to stay on for USB charger</li>
+ * <li>{@link BatteryManager#BATTERY_PLUGGED_WIRELESS} to stay on for wireless charger</li>
+ * </ul>
+ * These values can be OR-ed together.
+ */
+ public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
+
+ /**
+ * Whether ADB is enabled.
+ */
+ public static final String ADB_ENABLED = "adb_enabled";
+
+ /**
+ * Whether assisted GPS should be enabled or not.
+ * @hide
+ */
+ public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+
+ /**
+ * Whether bluetooth is enabled/disabled
+ * 0=disabled. 1=enabled.
+ */
+ public static final String BLUETOOTH_ON = "bluetooth_on";
+
+ /**
+ * CDMA Cell Broadcast SMS
+ * 0 = CDMA Cell Broadcast SMS disabled
+ * 1 = CDMA Cell Broadcast SMS enabled
+ * @hide
+ */
+ public static final String CDMA_CELL_BROADCAST_SMS =
+ "cdma_cell_broadcast_sms";
+
+ /**
+ * The CDMA roaming mode 0 = Home Networks, CDMA default
+ * 1 = Roaming on Affiliated networks
+ * 2 = Roaming on any networks
+ * @hide
+ */
+ public static final String CDMA_ROAMING_MODE = "roaming_settings";
+
+ /**
+ * The CDMA subscription mode 0 = RUIM/SIM (default)
+ * 1 = NV
+ * @hide
+ */
+ public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
+
+ /** Inactivity timeout to track mobile data activity.
+ *
+ * If set to a positive integer, it indicates the inactivity timeout value in seconds to
+ * infer the data activity of mobile network. After a period of no activity on mobile
+ * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
+ * intent is fired to indicate a transition of network status from "active" to "idle". Any
+ * subsequent activity on mobile networks triggers the firing of {@code
+ * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
+ *
+ * Network activity refers to transmitting or receiving data on the network interfaces.
+ *
+ * Tracking is disabled if set to zero or negative value.
+ *
+ * @hide
+ */
+ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
+
+ /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
+ * but for Wifi network.
+ * @hide
+ */
+ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
+
+ /**
+ * Whether or not data roaming is enabled. (0 = false, 1 = true)
+ */
+ public static final String DATA_ROAMING = "data_roaming";
+
+ /**
+ * Whether user has enabled development settings.
+ */
+ public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+
+ /**
+ * Whether the device has been provisioned (0 = false, 1 = true)
+ */
+ public static final String DEVICE_PROVISIONED = "device_provisioned";
+
+ /**
+ * The saved value for WindowManagerService.setForcedDisplayDensity().
+ * One integer in dpi. If unset, then use the real display density.
+ * @hide
+ */
+ public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
+
+ /**
+ * The saved value for WindowManagerService.setForcedDisplaySize().
+ * Two integers separated by a comma. If unset, then use the real display size.
+ * @hide
+ */
+ public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
+
+ /**
+ * The maximum size, in bytes, of a download that the download manager will transfer over
+ * a non-wifi connection.
+ * @hide
+ */
+ public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
+ "download_manager_max_bytes_over_mobile";
+
+ /**
+ * The recommended maximum size, in bytes, of a download that the download manager should
+ * transfer over a non-wifi connection. Over this size, the use will be warned, but will
+ * have the option to start the download over the mobile connection anyway.
+ * @hide
+ */
+ public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
+ "download_manager_recommended_max_bytes_over_mobile";
+
+ /**
+ * Whether the package installer should allow installation of apps downloaded from
+ * sources other than Google Play.
+ *
+ * 1 = allow installing from other sources
+ * 0 = only allow installing from Google Play
+ */
+ public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+
+ /**
+ * Whether mobile data connections are allowed by the user. See
+ * ConnectivityManager for more info.
+ * @hide
+ */
+ public static final String MOBILE_DATA = "mobile_data";
+
+ /** {@hide} */
+ public static final String NETSTATS_ENABLED = "netstats_enabled";
+ /** {@hide} */
+ public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+ /** {@hide} */
+ public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
+ /** {@hide} */
+ public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
+ /** {@hide} */
+ public static final String NETSTATS_REPORT_XT_OVER_DEV = "netstats_report_xt_over_dev";
+
+ /** {@hide} */
+ public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
+ /** {@hide} */
+ public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
+
+ /** {@hide} */
+ public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
+ /** {@hide} */
+ public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
+
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
+
+ /**
+ * User preference for which network(s) should be used. Only the
+ * connectivity service should touch this.
+ */
+ public static final String NETWORK_PREFERENCE = "network_preference";
+
+ /**
+ * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
+ * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
+ * exceeded.
+ * @hide
+ */
+ public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
+
+ /**
+ * The length of time in milli-seconds that automatic small adjustments to
+ * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
+ * @hide
+ */
+ public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
+
+ /** Preferred NTP server. {@hide} */
+ public static final String NTP_SERVER = "ntp_server";
+ /** Timeout in milliseconds to wait for NTP server. {@hide} */
+ public static final String NTP_TIMEOUT = "ntp_timeout";
+
+ /**
+ * The interval in milliseconds at which to check packet counts on the
+ * mobile data interface when screen is on, to detect possible data
+ * connection problems.
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
+ "pdp_watchdog_poll_interval_ms";
+
+ /**
+ * The interval in milliseconds at which to check packet counts on the
+ * mobile data interface when screen is off, to detect possible data
+ * connection problems.
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
+ "pdp_watchdog_long_poll_interval_ms";
+
+ /**
+ * The interval in milliseconds at which to check packet counts on the
+ * mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT}
+ * outgoing packets has been reached without incoming packets.
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
+ "pdp_watchdog_error_poll_interval_ms";
+
+ /**
+ * The number of outgoing packets sent without seeing an incoming packet
+ * that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT}
+ * device is logged to the event log
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
+ "pdp_watchdog_trigger_packet_count";
+
+ /**
+ * The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS})
+ * after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before
+ * attempting data connection recovery.
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
+ "pdp_watchdog_error_poll_count";
+
+ /**
+ * The number of failed PDP reset attempts before moving to something more
+ * drastic: re-registering to the network.
+ * @hide
+ */
+ public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
+ "pdp_watchdog_max_pdp_reset_fail_count";
+
+ /**
+ * A positive value indicates how often the SamplingProfiler
+ * should take snapshots. Zero value means SamplingProfiler
+ * is disabled.
+ *
+ * @hide
+ */
+ public static final String SAMPLING_PROFILER_MS = "sampling_profiler_ms";
+
+ /**
+ * URL to open browser on to allow user to manage a prepay account
+ * @hide
+ */
+ public static final String SETUP_PREPAID_DATA_SERVICE_URL =
+ "setup_prepaid_data_service_url";
+
+ /**
+ * URL to attempt a GET on to see if this is a prepay device
+ * @hide
+ */
+ public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
+ "setup_prepaid_detection_target_url";
+
+ /**
+ * Host to check for a redirect to after an attempt to GET
+ * SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
+ * this is a prepaid device with zero balance.)
+ * @hide
+ */
+ public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
+ "setup_prepaid_detection_redir_host";
+
+ /**
+ * Used to disable Tethering on a device - defaults to true
+ * @hide
+ */
+ public static final String TETHER_SUPPORTED = "tether_supported";
+
+ /**
+ * Used to require DUN APN on the device or not - defaults to a build config value
+ * which defaults to false
+ * @hide
+ */
+ public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
+
+ /**
+ * Used to hold a gservices-provisioned apn value for DUN. If set, or the
+ * corresponding build config values are set it will override the APN DB
+ * values.
+ * Consists of a comma seperated list of strings:
+ * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+ * note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN"
+ * @hide
+ */
+ public static final String TETHER_DUN_APN = "tether_dun_apn";
+
+ /**
+ * The bandwidth throttle polling freqency in seconds
+ * @hide
+ */
+ public static final String THROTTLE_POLLING_SEC = "throttle_polling_sec";
+
+ /**
+ * The bandwidth throttle threshold (long)
+ * @hide
+ */
+ public static final String THROTTLE_THRESHOLD_BYTES = "throttle_threshold_bytes";
+
+ /**
+ * The bandwidth throttle value (kbps)
+ * @hide
+ */
+ public static final String THROTTLE_VALUE_KBITSPS = "throttle_value_kbitsps";
+
+ /**
+ * The bandwidth throttle reset calendar day (1-28)
+ * @hide
+ */
+ public static final String THROTTLE_RESET_DAY = "throttle_reset_day";
+
+ /**
+ * The throttling notifications we should send
+ * @hide
+ */
+ public static final String THROTTLE_NOTIFICATION_TYPE = "throttle_notification_type";
+
+ /**
+ * Help URI for data throttling policy
+ * @hide
+ */
+ public static final String THROTTLE_HELP_URI = "throttle_help_uri";
+
+ /**
+ * The length of time in Sec that we allow our notion of NTP time
+ * to be cached before we refresh it
+ * @hide
+ */
+ public static final String THROTTLE_MAX_NTP_CACHE_AGE_SEC =
+ "throttle_max_ntp_cache_age_sec";
+
+ /**
+ * USB Mass Storage Enabled
+ */
+ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+
+ /**
+ * If this setting is set (to anything), then all references
+ * to Gmail on the device must change to Google Mail.
+ */
+ public static final String USE_GOOGLE_MAIL = "use_google_mail";
+
+ /** Autofill server address (Used in WebView/browser).
+ * {@hide} */
+ public static final String WEB_AUTOFILL_QUERY_URL =
+ "web_autofill_query_url";
+
+ /**
+ * Whether to notify the user of open networks.
+ * <p>
+ * If not connected and the scan results have an open network, we will
+ * put this notification up. If we attempt to connect to a network or
+ * the open network(s) disappear, we remove the notification. When we
+ * show the notification, we will not show it again for
+ * {@link android.provider.Settings.Secure#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} time.
+ */
+ public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+ "wifi_networks_available_notification_on";
+ /**
+ * {@hide}
+ */
+ public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+ "wimax_networks_available_notification_on";
+
+ /**
+ * Delay (in seconds) before repeating the Wi-Fi networks available notification.
+ * Connecting to a network will reset the timer.
+ */
+ public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
+ "wifi_networks_available_repeat_delay";
+
+ /**
+ * 802.11 country code in ISO 3166 format
+ * @hide
+ */
+ public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
+
+ /**
+ * The interval in milliseconds to issue wake up scans when wifi needs
+ * to connect. This is necessary to connect to an access point when
+ * device is on the move and the screen is off.
+ * @hide
+ */
+ public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
+ "wifi_framework_scan_interval_ms";
+
+ /**
+ * The interval in milliseconds after which Wi-Fi is considered idle.
+ * When idle, it is possible for the device to be switched from Wi-Fi to
+ * the mobile data network.
+ * @hide
+ */
+ public static final String WIFI_IDLE_MS = "wifi_idle_ms";
+
+ /**
+ * When the number of open networks exceeds this number, the
+ * least-recently-used excess networks will be removed.
+ */
+ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+
+ /**
+ * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this.
+ */
+ public static final String WIFI_ON = "wifi_on";
+
+ /**
+ * Used to save the Wifi_ON state prior to tethering.
+ * This state will be checked to restore Wifi after
+ * the user turns off tethering.
+ *
+ * @hide
+ */
+ public static final String WIFI_SAVED_STATE = "wifi_saved_state";
+
+ /**
+ * The interval in milliseconds to scan as used by the wifi supplicant
+ * @hide
+ */
+ public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
+ "wifi_supplicant_scan_interval_ms";
+
+ /**
+ * Whether the Wi-Fi watchdog is enabled.
+ */
+ public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
+
+ /**
+ * ms delay interval between rssi polling when the signal is known to be weak
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS =
+ "wifi_watchdog_rssi_fetch_interval_ms";
+
+ /**
+ * Number of ARP pings per check.
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_NUM_ARP_PINGS = "wifi_watchdog_num_arp_pings";
+
+ /**
+ * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
+ * the setting needs to be set to 0 to disable it.
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
+ "wifi_watchdog_poor_network_test_enabled";
+
+ /**
+ * Setting to turn on suspend optimizations at screen off on Wi-Fi. Enabled by default and
+ * needs to be set to 0 to disable it.
+ * @hide
+ */
+ public static final String WIFI_SUSPEND_OPTIMIZATIONS_ENABLED =
+ "wifi_suspend_optimizations_enabled";
+
+ /**
+ * The maximum number of times we will retry a connection to an access
+ * point for which we have failed in acquiring an IP address from DHCP.
+ * A value of N means that we will make N+1 connection attempts in all.
+ */
+ public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+
+ /**
+ * Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile
+ * data connectivity to be established after a disconnect from Wi-Fi.
+ */
+ public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
+ "wifi_mobile_data_transition_wakelock_timeout_ms";
+
+ /**
+ * The operational wifi frequency band
+ * Set to one of {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ} or
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ}
+ *
+ * @hide
+ */
+ public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band";
+
+ /**
+ * The Wi-Fi peer-to-peer device name
+ * @hide
+ */
+ public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
+
+ /**
+ * Nonzero causes Log.wtf() to crash.
+ * @hide
+ */
+ public static final String WTF_IS_FATAL = "wtf_is_fatal";
+
+
+
+
+ // Populated lazily, guarded by class object:
+ private static NameValueCache sNameValueCache = null;
+
+ private static void lazyInitCache() {
+ if (sNameValueCache == null) {
+ sNameValueCache = new NameValueCache(
+ SYS_PROP_SETTING_VERSION,
+ CONTENT_URI,
+ CALL_METHOD_GET_GLOBAL,
+ CALL_METHOD_PUT_GLOBAL);
+ }
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param resolver to access the database with
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ public synchronized static String getString(ContentResolver resolver, String name) {
+ return getStringForUser(resolver, name, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public synchronized static String getStringForUser(ContentResolver resolver, String name,
+ int userHandle) {
+ lazyInitCache();
+ return sNameValueCache.getStringForUser(resolver, name, userHandle);
+ }
+
+ /**
+ * Store a name/value pair into the database.
+ * @param resolver to access the database with
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ public static boolean putString(ContentResolver resolver,
+ String name, String value) {
+ return putStringForUser(resolver, name, value, UserHandle.myUserId());
+ }
+
+ /** @hide */
+ public static boolean putStringForUser(ContentResolver resolver,
+ String name, String value, int userHandle) {
+ lazyInitCache();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Global.putString(name=" + name + ", value=" + value
+ + " for " + userHandle);
+ }
+ return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
+ }
+
+ /**
+ * Construct the content URI for a particular name/value pair,
+ * useful for monitoring changes with a ContentObserver.
+ * @param name to look up in the table
+ * @return the corresponding content URI, or null if not present
+ */
+ public static Uri getUriFor(String name) {
+ return getUriFor(CONTENT_URI, name);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you. The default value will be returned if the setting is
+ * not defined or not an integer.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid integer.
+ */
+ public static int getInt(ContentResolver cr, String name, int def) {
+ String v = getString(cr, name);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as an integer. Note that internally setting values are always
+ * stored as strings; this function converts the string to an integer
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link SettingNotFoundException}.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ *
+ * @return The setting's current value.
+ */
+ public static int getInt(ContentResolver cr, String name)
+ throws SettingNotFoundException {
+ String v = getString(cr, name);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as an
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ public static boolean putInt(ContentResolver cr, String name, int value) {
+ return putString(cr, name, Integer.toString(value));
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you. The default value will be returned if the setting is
+ * not defined or not a {@code long}.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid {@code long}.
+ */
+ public static long getLong(ContentResolver cr, String name, long def) {
+ String valString = getString(cr, name);
+ long value;
+ try {
+ value = valString != null ? Long.parseLong(valString) : def;
+ } catch (NumberFormatException e) {
+ value = def;
+ }
+ return value;
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a {@code long}. Note that internally setting values are always
+ * stored as strings; this function converts the string to a {@code long}
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link SettingNotFoundException}.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ *
+ * @return The setting's current value.
+ * @throws SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not an integer.
+ */
+ public static long getLong(ContentResolver cr, String name)
+ throws SettingNotFoundException {
+ String valString = getString(cr, name);
+ try {
+ return Long.parseLong(valString);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a secure settings value as a long
+ * integer. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ public static boolean putLong(ContentResolver cr, String name, long value) {
+ return putString(cr, name, Long.toString(value));
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a floating point number. Note that internally setting values are
+ * always stored as strings; this function converts the string to an
+ * float for you. The default value will be returned if the setting
+ * is not defined or not a valid float.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid float.
+ */
+ public static float getFloat(ContentResolver cr, String name, float def) {
+ String v = getString(cr, name);
+ try {
+ return v != null ? Float.parseFloat(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a float. Note that internally setting values are always
+ * stored as strings; this function converts the string to a float
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link SettingNotFoundException}.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not a float.
+ *
+ * @return The setting's current value.
+ */
+ public static float getFloat(ContentResolver cr, String name)
+ throws SettingNotFoundException {
+ String v = getString(cr, name);
+ if (v == null) {
+ throw new SettingNotFoundException(name);
+ }
+ try {
+ return Float.parseFloat(v);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(name);
+ }
+ }
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * floating point number. This will either create a new entry in the
+ * table if the given name does not exist, or modify the value of the
+ * existing row with that name. Note that internally setting values
+ * are always stored as strings, so this function converts the given
+ * value to a string before storing it.
+ *
+ * @param cr The ContentResolver to access.
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ public static boolean putFloat(ContentResolver cr, String name, float value) {
+ return putString(cr, name, Float.toString(value));
+ }
+ }
+
+ /**
* User-defined bookmarks and shortcuts. The target of each bookmark is an
* Intent URL, allowing it to be either a web page or a particular
* application activity.
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index df85b2f..bc3efdd 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -140,7 +140,7 @@
// Inform all listeners that the list of searchables has been updated.
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
@@ -165,7 +165,7 @@
}
Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index 4e00ef9..30ca340 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -27,6 +27,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.Settings;
@@ -70,7 +71,7 @@
"com.google.android.providers.enhancedgooglesearch/.Launcher";
// Cache the package manager instance
- private IPackageManager mPm;
+ final private IPackageManager mPm;
// User for which this Searchables caches information
private int mUserId;
@@ -81,6 +82,7 @@
public Searchables (Context context, int userId) {
mContext = context;
mUserId = userId;
+ mPm = AppGlobals.getPackageManager();
}
/**
@@ -125,50 +127,50 @@
ActivityInfo ai = null;
try {
- ai = mContext.getPackageManager().
- getActivityInfo(activity, PackageManager.GET_META_DATA );
- String refActivityName = null;
+ ai = mPm.getActivityInfo(activity, PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error getting activity info " + re);
+ return null;
+ }
+ String refActivityName = null;
- // First look for activity-specific reference
- Bundle md = ai.metaData;
+ // First look for activity-specific reference
+ Bundle md = ai.metaData;
+ if (md != null) {
+ refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
+ }
+ // If not found, try for app-wide reference
+ if (refActivityName == null) {
+ md = ai.applicationInfo.metaData;
if (md != null) {
refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
}
- // If not found, try for app-wide reference
- if (refActivityName == null) {
- md = ai.applicationInfo.metaData;
- if (md != null) {
- refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
- }
+ }
+
+ // Irrespective of source, if a reference was found, follow it.
+ if (refActivityName != null)
+ {
+ // This value is deprecated, return null
+ if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
+ return null;
+ }
+ String pkg = activity.getPackageName();
+ ComponentName referredActivity;
+ if (refActivityName.charAt(0) == '.') {
+ referredActivity = new ComponentName(pkg, pkg + refActivityName);
+ } else {
+ referredActivity = new ComponentName(pkg, refActivityName);
}
- // Irrespective of source, if a reference was found, follow it.
- if (refActivityName != null)
- {
- // This value is deprecated, return null
- if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
- return null;
- }
- String pkg = activity.getPackageName();
- ComponentName referredActivity;
- if (refActivityName.charAt(0) == '.') {
- referredActivity = new ComponentName(pkg, pkg + refActivityName);
- } else {
- referredActivity = new ComponentName(pkg, refActivityName);
- }
-
- // Now try the referred activity, and if found, cache
- // it against the original name so we can skip the check
- synchronized (this) {
- result = mSearchablesMap.get(referredActivity);
- if (result != null) {
- mSearchablesMap.put(activity, result);
- return result;
- }
+ // Now try the referred activity, and if found, cache
+ // it against the original name so we can skip the check
+ synchronized (this) {
+ result = mSearchablesMap.get(referredActivity);
+ if (result != null) {
+ mSearchablesMap.put(activity, result);
+ return result;
}
}
- } catch (PackageManager.NameNotFoundException e) {
- // case 3: no metadata
}
// Step 3. None found. Return null.
@@ -208,6 +210,7 @@
// Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
List<ResolveInfo> searchList;
final Intent intent = new Intent(Intent.ACTION_SEARCH);
+
searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA);
List<ResolveInfo> webSearchInfoList;
@@ -219,6 +222,7 @@
int search_count = (searchList == null ? 0 : searchList.size());
int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size());
int count = search_count + web_search_count;
+ long token = Binder.clearCallingIdentity();
for (int ii = 0; ii < count; ii++) {
// for each component, try to find metadata
ResolveInfo info = (ii < search_count)
@@ -237,6 +241,7 @@
}
}
}
+ Binder.restoreCallingIdentity(token);
}
List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities();
@@ -391,9 +396,6 @@
}
private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
- if (mPm == null) {
- mPm = AppGlobals.getPackageManager();
- }
List<ResolveInfo> activities = null;
try {
activities =
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index 47e2129..186cb49 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -69,7 +69,8 @@
// Need to be seen by stop() which can be called from another thread. mAudioTrack will be
// set to null only after waitAndRelease().
- private volatile AudioTrack mAudioTrack;
+ private Object mAudioTrackLock = new Object();
+ private AudioTrack mAudioTrack;
private volatile boolean mStopped;
BlockingAudioTrack(int streamType, int sampleRate,
@@ -93,7 +94,9 @@
public boolean init() {
AudioTrack track = createStreamingAudioTrack();
- mAudioTrack = track;
+ synchronized (mAudioTrackLock) {
+ mAudioTrack = track;
+ }
if (track == null) {
return false;
@@ -103,24 +106,34 @@
}
public void stop() {
- AudioTrack track = mAudioTrack;
- if (track != null) {
- track.stop();
+ synchronized (mAudioTrackLock) {
+ if (mAudioTrack != null) {
+ mAudioTrack.stop();
+ }
+ mStopped = true;
}
- mStopped = true;
}
public int write(byte[] data) {
- if (mAudioTrack == null || mStopped) {
+ AudioTrack track = null;
+ synchronized (mAudioTrackLock) {
+ track = mAudioTrack;
+ }
+
+ if (track == null || mStopped) {
return -1;
}
- final int bytesWritten = writeToAudioTrack(mAudioTrack, data);
+ final int bytesWritten = writeToAudioTrack(track, data);
+
mBytesWritten += bytesWritten;
return bytesWritten;
}
public void waitAndRelease() {
- AudioTrack track = mAudioTrack;
+ AudioTrack track = null;
+ synchronized (mAudioTrackLock) {
+ track = mAudioTrack;
+ }
if (track == null) {
if (DBG) Log.d(TAG, "Audio track null [duplicate call to waitAndRelease ?]");
return;
@@ -152,8 +165,10 @@
// all data from the audioTrack has been sent to the mixer, so
// it's safe to release at this point.
if (DBG) Log.d(TAG, "Releasing audio track [" + track.hashCode() + "]");
+ synchronized(mAudioTrackLock) {
+ mAudioTrack = null;
+ }
track.release();
- mAudioTrack = null;
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 506594b..85e4b9d 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -206,13 +206,52 @@
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
- density = noncompatDensity = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
- densityDpi = noncompatDensityDpi = DENSITY_DEVICE;
+ density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
+ densityDpi = DENSITY_DEVICE;
scaledDensity = density;
- xdpi = noncompatXdpi = DENSITY_DEVICE;
- ydpi = noncompatYdpi = DENSITY_DEVICE;
- noncompatWidthPixels = 0;
- noncompatHeightPixels = 0;
+ xdpi = DENSITY_DEVICE;
+ ydpi = DENSITY_DEVICE;
+ noncompatWidthPixels = widthPixels;
+ noncompatHeightPixels = heightPixels;
+ noncompatDensity = density;
+ noncompatDensityDpi = densityDpi;
+ noncompatScaledDensity = scaledDensity;
+ noncompatXdpi = xdpi;
+ noncompatYdpi = ydpi;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayMetrics && equals((DisplayMetrics)o);
+ }
+
+ /**
+ * Returns true if these display metrics equal the other display metrics.
+ *
+ * @param other The display metrics with which to compare.
+ * @return True if the display metrics are equal.
+ */
+ public boolean equals(DisplayMetrics other) {
+ return other != null
+ && widthPixels == other.widthPixels
+ && heightPixels == other.heightPixels
+ && density == other.density
+ && densityDpi == other.densityDpi
+ && scaledDensity == other.scaledDensity
+ && xdpi == other.xdpi
+ && ydpi == other.ydpi
+ && noncompatWidthPixels == other.noncompatWidthPixels
+ && noncompatHeightPixels == other.noncompatHeightPixels
+ && noncompatDensity == other.noncompatDensity
+ && noncompatDensityDpi == other.noncompatDensityDpi
+ && noncompatScaledDensity == other.noncompatScaledDensity
+ && noncompatXdpi == other.noncompatXdpi
+ && noncompatYdpi == other.noncompatYdpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return widthPixels * heightPixels * densityDpi;
}
@Override
diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java
index bf25306..6027d08 100644
--- a/core/java/android/util/Pair.java
+++ b/core/java/android/util/Pair.java
@@ -16,6 +16,8 @@
package android.util;
+import libcore.util.Objects;
+
/**
* Container to ease passing around a tuple of two objects. This object provides a sensible
* implementation of equals(), returning true if equals() is true on each of the contained
@@ -26,8 +28,8 @@
public final S second;
/**
- * Constructor for a Pair. If either are null then equals() and hashCode() will throw
- * a NullPointerException.
+ * Constructor for a Pair.
+ *
* @param first the first object in the Pair
* @param second the second object in the pair
*/
@@ -37,31 +39,30 @@
}
/**
- * Checks the two objects for equality by delegating to their respective equals() methods.
- * @param o the Pair to which this one is to be checked for equality
- * @return true if the underlying objects of the Pair are both considered equals()
+ * Checks the two objects for equality by delegating to their respective
+ * {@link Object#equals(Object)} methods.
+ *
+ * @param o the {@link Pair} to which this one is to be checked for equality
+ * @return true if the underlying objects of the Pair are both considered
+ * equal
*/
+ @Override
public boolean equals(Object o) {
- if (o == this) return true;
- if (!(o instanceof Pair)) return false;
- final Pair<F, S> other;
- try {
- other = (Pair<F, S>) o;
- } catch (ClassCastException e) {
+ if (!(o instanceof Pair)) {
return false;
}
- return first.equals(other.first) && second.equals(other.second);
+ Pair<?, ?> p = (Pair<?, ?>) o;
+ return Objects.equal(p.first, first) && Objects.equal(p.second, second);
}
/**
* Compute a hash code using the hash codes of the underlying objects
+ *
* @return a hashcode of the Pair
*/
+ @Override
public int hashCode() {
- int result = 17;
- result = 31 * result + first.hashCode();
- result = 31 * result + second.hashCode();
- return result;
+ return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
}
/**
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 392d1f2..6848606 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -17,6 +17,7 @@
package android.view;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -166,8 +167,7 @@
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
- Display d = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- mFrameIntervalNanos = (long)(1000000000 / d.getRefreshRate());
+ mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
@@ -175,6 +175,12 @@
}
}
+ private static float getRefreshRate() {
+ DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
+ Display.DEFAULT_DISPLAY);
+ return di.refreshRate;
+ }
+
/**
* Gets the choreographer for the calling thread. Must be called from
* a thread that already has a {@link android.os.Looper} associated with it.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 6f8ca13..8ac84f7 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,7 +19,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -49,10 +49,16 @@
*/
public final class Display {
private static final String TAG = "Display";
+ private static final boolean DEBUG = false;
+ private final DisplayManagerGlobal mGlobal;
private final int mDisplayId;
+ private final int mLayerStack;
+ private final String mName;
private final CompatibilityInfoHolder mCompatibilityInfo;
- private final DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ private DisplayInfo mDisplayInfo; // never null
+ private boolean mIsValid;
// Temporary display metrics structure used for compatibility mode.
private final DisplayMetrics mTempMetrics = new DisplayMetrics();
@@ -80,9 +86,16 @@
*
* @hide
*/
- public Display(int displayId, CompatibilityInfoHolder compatibilityInfo) {
+ public Display(DisplayManagerGlobal global,
+ int displayId, DisplayInfo displayInfo /*not null*/,
+ CompatibilityInfoHolder compatibilityInfo) {
+ mGlobal = global;
mDisplayId = displayId;
+ mDisplayInfo = displayInfo;
+ mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
+ mName = displayInfo.name; // cannot change as long as the display is valid
mCompatibilityInfo = compatibilityInfo;
+ mIsValid = true;
}
/**
@@ -97,15 +110,37 @@
}
/**
+ * Returns true if this display is still valid, false if the display has been removed.
+ *
+ * If the display is invalid, then the methods of this class will
+ * continue to report the most recently observed display information.
+ * However, it is unwise (and rather fruitless) to continue using a
+ * {@link Display} object after the display's demise.
+ *
+ * It's possible for a display that was previously invalid to become
+ * valid again if a display with the same id is reconnected.
+ *
+ * @return True if the display is still valid.
+ */
+ public boolean isValid() {
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mIsValid;
+ }
+ }
+
+ /**
* Gets a full copy of the display information.
*
* @param outDisplayInfo The object to receive the copy of the display information.
+ * @return True if the display is still valid.
* @hide
*/
- public void getDisplayInfo(DisplayInfo outDisplayInfo) {
+ public boolean getDisplayInfo(DisplayInfo outDisplayInfo) {
synchronized (this) {
updateDisplayInfoLocked();
outDisplayInfo.copyFrom(mDisplayInfo);
+ return mIsValid;
}
}
@@ -115,13 +150,11 @@
* Each display has its own independent layer stack upon which surfaces
* are placed to be managed by surface flinger.
*
- * @return The layer stack number.
+ * @return The display's layer stack number.
* @hide
*/
public int getLayerStack() {
- // Note: This is the current convention but there is no requirement that
- // the display id and layer stack id be the same.
- return mDisplayId;
+ return mLayerStack;
}
/**
@@ -135,6 +168,14 @@
}
/**
+ * Gets the name of the display.
+ * @return The display's name.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
* Gets the size of the display, in pixels.
* <p>
* Note that this value should <em>not</em> be used for computing layouts,
@@ -366,9 +407,25 @@
}
private void updateDisplayInfoLocked() {
- // TODO: only refresh the display information when needed
- if (!DisplayManager.getInstance().getDisplayInfo(mDisplayId, mDisplayInfo)) {
- Log.e(TAG, "Could not get information about logical display " + mDisplayId);
+ // Note: The display manager caches display info objects on our behalf.
+ DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
+ if (newInfo == null) {
+ // Preserve the old mDisplayInfo after the display is removed.
+ if (mIsValid) {
+ mIsValid = false;
+ if (DEBUG) {
+ Log.d(TAG, "Logical display " + mDisplayId + " was removed.");
+ }
+ }
+ } else {
+ // Use the new display info. (It might be the same object if nothing changed.)
+ mDisplayInfo = newInfo;
+ if (!mIsValid) {
+ mIsValid = true;
+ if (DEBUG) {
+ Log.d(TAG, "Logical display " + mDisplayId + " was recreated.");
+ }
+ }
}
}
@@ -390,7 +447,7 @@
updateDisplayInfoLocked();
mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo);
return "Display id " + mDisplayId + ": " + mDisplayInfo
- + ", " + mTempMetrics;
+ + ", " + mTempMetrics + ", isValid=" + mIsValid;
}
}
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 6c2e540..0b138c2 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -66,7 +66,7 @@
@Override
protected void finalize() throws Throwable {
try {
- dispose();
+ dispose(true);
} finally {
super.finalize();
}
@@ -76,9 +76,17 @@
* Disposes the receiver.
*/
public void dispose() {
+ dispose(false);
+ }
+
+ private void dispose(boolean finalized) {
if (mCloseGuard != null) {
+ if (finalized) {
+ mCloseGuard.warnIfOpen();
+ }
mCloseGuard.close();
}
+
if (mReceiverPtr != 0) {
nativeDispose(mReceiverPtr);
mReceiverPtr = 0;
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index e38f245..b728d71 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -21,12 +21,24 @@
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import libcore.util.Objects;
+
/**
* Describes the characteristics of a particular logical display.
* @hide
*/
public final class DisplayInfo implements Parcelable {
/**
+ * The surface flinger layer stack associated with this logical display.
+ */
+ public int layerStack;
+
+ /**
+ * The human-readable name of the display.
+ */
+ public String name;
+
+ /**
* The width of the portion of the display that is available to applications, in pixels.
* Represents the size of the display minus any system decorations.
*/
@@ -138,16 +150,46 @@
public DisplayInfo() {
}
+ public DisplayInfo(DisplayInfo other) {
+ copyFrom(other);
+ }
+
private DisplayInfo(Parcel source) {
readFromParcel(source);
}
@Override
- public int describeContents() {
- return 0;
+ public boolean equals(Object o) {
+ return o instanceof DisplayInfo && equals((DisplayInfo)o);
+ }
+
+ public boolean equals(DisplayInfo other) {
+ return other != null
+ && layerStack == other.layerStack
+ && Objects.equal(name, other.name)
+ && appWidth == other.appWidth
+ && appHeight == other.appHeight
+ && smallestNominalAppWidth == other.smallestNominalAppWidth
+ && smallestNominalAppHeight == other.smallestNominalAppHeight
+ && largestNominalAppWidth == other.largestNominalAppWidth
+ && largestNominalAppHeight == other.largestNominalAppHeight
+ && logicalWidth == other.logicalWidth
+ && logicalHeight == other.logicalHeight
+ && rotation == other.rotation
+ && refreshRate == other.refreshRate
+ && logicalDensityDpi == other.logicalDensityDpi
+ && physicalXDpi == other.physicalXDpi
+ && physicalYDpi == other.physicalYDpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
}
public void copyFrom(DisplayInfo other) {
+ layerStack = other.layerStack;
+ name = other.name;
appWidth = other.appWidth;
appHeight = other.appHeight;
smallestNominalAppWidth = other.smallestNominalAppWidth;
@@ -164,6 +206,8 @@
}
public void readFromParcel(Parcel source) {
+ layerStack = source.readInt();
+ name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
smallestNominalAppWidth = source.readInt();
@@ -181,6 +225,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(layerStack);
+ dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
dest.writeInt(smallestNominalAppWidth);
@@ -196,6 +242,11 @@
dest.writeFloat(physicalYDpi);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfoHolder cih) {
getMetricsWithSize(outMetrics, cih, appWidth, appHeight);
}
@@ -227,13 +278,14 @@
// For debugging purposes
@Override
public String toString() {
- return "app " + appWidth + " x " + appHeight
+ return "DisplayInfo{\"" + name + "\", app " + appWidth + " x " + appHeight
+ ", real " + logicalWidth + " x " + logicalHeight
+ ", largest app " + largestNominalAppWidth + " x " + largestNominalAppHeight
+ ", smallest app " + smallestNominalAppWidth + " x " + smallestNominalAppHeight
+ ", " + refreshRate + " fps"
+ ", rotation " + rotation
+ ", density " + logicalDensityDpi
- + ", " + physicalXDpi + " x " + physicalYDpi + " dpi";
+ + ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
+ + ", layerStack " + layerStack + "}";
}
}
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 2d2e8e4..3bdd5c0 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -25,9 +25,12 @@
* An implementation of display list for OpenGL ES 2.0.
*/
class GLES20DisplayList extends DisplayList {
- // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
- // as the DisplayList is alive. The Bitmaps are populated by the GLES20RecordingCanvas.
+ // These lists ensure that any Bitmaps and DisplayLists recorded by a DisplayList are kept
+ // alive as long as the DisplayList is alive. The Bitmap and DisplayList lists
+ // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
+ // cleared at the start of a new drawing frame or when the view is detached from the window.
final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
+ final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
private GLES20RecordingCanvas mCanvas;
private boolean mValid;
@@ -79,6 +82,7 @@
public void clear() {
if (!mValid) {
mBitmaps.clear();
+ mChildDisplayLists.clear();
}
}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index c9ba65f..ba8be6c 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -76,6 +76,7 @@
void start() {
mDisplayList.mBitmaps.clear();
+ mDisplayList.mChildDisplayLists.clear();
}
int end(int nativeDisplayList) {
@@ -156,6 +157,13 @@
}
@Override
+ public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) {
+ int status = super.drawDisplayList(displayList, dirty, flags);
+ mDisplayList.mChildDisplayLists.add(displayList);
+ return status;
+ }
+
+ @Override
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
super.drawLine(startX, startY, stopX, stopY, paint);
recordShaderBitmap(paint);
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 0114a41..4bbdd4e 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -226,17 +226,12 @@
*/
private boolean mIsDoubleTapping;
- private float mLastMotionY;
- private float mLastMotionX;
+ private float mLastFocusX;
+ private float mLastFocusY;
+ private float mDownFocusX;
+ private float mDownFocusY;
private boolean mIsLongpressEnabled;
-
- /**
- * True if we are at a target API level of >= Froyo or the developer can
- * explicitly set it. If true, input events with > 1 pointer will be ignored
- * so we can work side by side with multitouch gesture detectors.
- */
- private boolean mIgnoreMultitouch;
/**
* Determines speed during touch scrolling
@@ -349,8 +344,16 @@
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
- this(context, listener, handler, context != null &&
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
+ if (handler != null) {
+ mHandler = new GestureHandler(handler);
+ } else {
+ mHandler = new GestureHandler();
+ }
+ mListener = listener;
+ if (listener instanceof OnDoubleTapListener) {
+ setOnDoubleTapListener((OnDoubleTapListener) listener);
+ }
+ init(context);
}
/**
@@ -362,31 +365,19 @@
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
- * @param ignoreMultitouch whether events involving more than one pointer should
- * be ignored.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
- boolean ignoreMultitouch) {
- if (handler != null) {
- mHandler = new GestureHandler(handler);
- } else {
- mHandler = new GestureHandler();
- }
- mListener = listener;
- if (listener instanceof OnDoubleTapListener) {
- setOnDoubleTapListener((OnDoubleTapListener) listener);
- }
- init(context, ignoreMultitouch);
+ boolean unused) {
+ this(context, listener, handler);
}
- private void init(Context context, boolean ignoreMultitouch) {
+ private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
- mIgnoreMultitouch = ignoreMultitouch;
// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
@@ -456,34 +447,41 @@
}
final int action = ev.getAction();
- final float y = ev.getY();
- final float x = ev.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
+ final boolean pointerUp =
+ (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
- if (mIgnoreMultitouch) {
- // Multitouch event - abort.
- cancel();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ // Cancel long press and taps
+ cancelTaps();
break;
case MotionEvent.ACTION_POINTER_UP:
- // Ending a multitouch gesture and going back to 1 finger
- if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
- int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
- mLastMotionX = ev.getX(index);
- mLastMotionY = ev.getY(index);
- mVelocityTracker.recycle();
- mVelocityTracker = VelocityTracker.obtain();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
break;
case MotionEvent.ACTION_DOWN:
@@ -504,8 +502,8 @@
}
}
- mLastMotionX = x;
- mLastMotionY = y;
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
@@ -525,22 +523,22 @@
break;
case MotionEvent.ACTION_MOVE:
- if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
+ if (mInLongPress) {
break;
}
- final float scrollX = mLastMotionX - x;
- final float scrollY = mLastMotionY - y;
+ final float scrollX = mLastFocusX - focusX;
+ final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
- final int deltaX = (int) (x - mCurrentDownEvent.getX());
- final int deltaY = (int) (y - mCurrentDownEvent.getY());
+ final int deltaX = (int) (focusX - mDownFocusX);
+ final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
@@ -551,8 +549,8 @@
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
}
break;
@@ -571,9 +569,10 @@
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
+ final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final float velocityY = velocityTracker.getYVelocity();
- final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity(pointerId);
+ final float velocityX = velocityTracker.getXVelocity(pointerId);
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
@@ -622,6 +621,18 @@
}
}
+ private void cancelTaps() {
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
+ mIsDoubleTapping = false;
+ mAlwaysInTapRegion = false;
+ mAlwaysInBiggerTapRegion = false;
+ if (mInLongPress) {
+ mInLongPress = false;
+ }
+ }
+
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
diff --git a/core/java/android/view/IDisplayContentChangeListener.aidl b/core/java/android/view/IDisplayContentChangeListener.aidl
new file mode 100644
index 0000000..8f23ff6
--- /dev/null
+++ b/core/java/android/view/IDisplayContentChangeListener.aidl
@@ -0,0 +1,32 @@
+/*
+** 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.
+*/
+
+package android.view;
+
+import android.os.IBinder;
+import android.view.WindowInfo;
+import android.graphics.Rect;
+
+/**
+ * Interface for observing content changes on a display.
+ *
+ * {@hide}
+ */
+oneway interface IDisplayContentChangeListener {
+ void onWindowTransition(int displayId, int transition, in WindowInfo info);
+ void onRectangleOnScreenRequested(int displayId, in Rect rectangle, boolean immediate);
+ void onRotationChanged(int rotation);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7f2de50..b9f3942 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
+import android.view.IDisplayContentChangeListener;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
@@ -34,7 +35,8 @@
import android.view.MotionEvent;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.IInputFilter;
+import android.view.IInputFilter;
+import android.view.WindowInfo;
/**
* System private interface to the window manager.
@@ -214,11 +216,6 @@
IBinder getFocusedWindowToken();
/**
- * Gets the frame on the screen of the window given its token.
- */
- boolean getWindowFrame(IBinder token, out Rect outBounds);
-
- /**
* Gets the compatibility scale of e window given its token.
*/
float getWindowCompatibilityScale(IBinder windowToken);
@@ -227,4 +224,29 @@
* Sets an input filter for manipulating the input event stream.
*/
void setInputFilter(in IInputFilter filter);
+
+ /**
+ * Sets the scale and offset for implementing accessibility magnification.
+ */
+ void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY);
+
+ /**
+ * Adds a listener for display content changes.
+ */
+ void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
+
+ /**
+ * Removes a listener for display content changes.
+ */
+ void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
+
+ /**
+ * Gets the info for a window given its token.
+ */
+ WindowInfo getWindowInfo(IBinder token);
+
+ /**
+ * Gets the infos for all visible windows.
+ */
+ void getVisibleWindowsForDisplay(int displayId, out List<WindowInfo> outInfos);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index c5d9255..ff9dcce 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -180,4 +180,9 @@
void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
float dsdx, float dtdx, float dsdy, float dtdy);
+
+ /**
+ * Notifies that a rectangle on the screen has been requested.
+ */
+ void onRectangleOnScreenRequested(IBinder token, in Rect rectangle, boolean immediate);
}
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 9c56782..117c101 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -73,7 +73,7 @@
@Override
protected void finalize() throws Throwable {
try {
- dispose();
+ dispose(true);
} finally {
super.finalize();
}
@@ -83,9 +83,17 @@
* Disposes the receiver.
*/
public void dispose() {
+ dispose(false);
+ }
+
+ private void dispose(boolean finalized) {
if (mCloseGuard != null) {
+ if (finalized) {
+ mCloseGuard.warnIfOpen();
+ }
mCloseGuard.close();
}
+
if (mReceiverPtr != 0) {
nativeDispose(mReceiverPtr);
mReceiverPtr = 0;
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 73f94bc..bcb8800 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,14 +17,13 @@
package android.view;
import android.content.Context;
-import android.util.DisplayMetrics;
import android.util.FloatMath;
-import android.util.Log;
/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
- * callback will notify users when a particular gesture event has occurred.
+ * Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
+ * The {@link OnScaleGestureListener} callback will notify users when a particular
+ * gesture event has occurred.
+ *
* This class should only be used with {@link MotionEvent}s reported via touch.
*
* To use this class:
@@ -87,8 +86,8 @@
* pointers going up.
*
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
- * and {@link ScaleGestureDetector#getFocusY()} will return the location
- * of the pointer remaining on the screen.
+ * and {@link ScaleGestureDetector#getFocusY()} will return focal point
+ * of the pointers remaining on the screen.
*
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
@@ -121,43 +120,23 @@
}
}
- /**
- * This value is the threshold ratio between our previous combined pressure
- * and the current combined pressure. We will only fire an onScale event if
- * the computed ratio between the current and previous event pressures is
- * greater than this value. When pressure decreases rapidly between events
- * the position values can often be imprecise, as it usually indicates
- * that the user is in the process of lifting a pointer off of the device.
- * Its value was tuned experimentally.
- */
- private static final float PRESSURE_THRESHOLD = 0.67f;
-
private final Context mContext;
private final OnScaleGestureListener mListener;
- private boolean mGestureInProgress;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
private float mFocusX;
private float mFocusY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
- private float mCurrPressure;
- private float mPrevPressure;
- private long mTimeDelta;
- private boolean mInvalidGesture;
-
- // Pointer IDs currently responsible for the two fingers controlling the gesture
- private int mActiveId0;
- private int mActiveId1;
- private boolean mActive0MostRecent;
+ private float mCurrSpan;
+ private float mPrevSpan;
+ private float mInitialSpan;
+ private float mCurrSpanX;
+ private float mCurrSpanY;
+ private float mPrevSpanX;
+ private float mPrevSpanY;
+ private long mCurrTime;
+ private long mPrevTime;
+ private boolean mInProgress;
+ private int mSpanSlop;
/**
* Consistency verifier for debugging purposes.
@@ -169,8 +148,21 @@
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
mContext = context;
mListener = listener;
+ mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
}
+ /**
+ * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
+ * when appropriate.
+ *
+ * <p>Applications should pass a complete and consistent event stream to this method.
+ * A complete and consistent event stream involves all MotionEvents from the initial
+ * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p>
+ *
+ * @param event The event to process
+ * @return true if the event was processed and the detector wants to receive the
+ * rest of the MotionEvents in this event stream.
+ */
public boolean onTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
@@ -178,265 +170,115 @@
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- reset(); // Start fresh
- }
-
- boolean handled = true;
- if (mInvalidGesture) {
- handled = false;
- } else if (!mGestureInProgress) {
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- mActiveId0 = event.getPointerId(0);
- mActive0MostRecent = true;
- }
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_POINTER_DOWN: {
- // We have a new multi-finger gesture
- if (mPrevEvent != null) mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- mTimeDelta = 0;
-
- int index1 = event.getActionIndex();
- int index0 = event.findPointerIndex(mActiveId0);
- mActiveId1 = event.getPointerId(index1);
- if (index0 < 0 || index0 == index1) {
- // Probably someone sending us a broken event stream.
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
- mActive0MostRecent = false;
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- break;
- }
- }
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN: {
- // End the old gesture and begin a new one with the most recent two fingers.
- mListener.onScaleEnd(this);
- final int oldActive0 = mActiveId0;
- final int oldActive1 = mActiveId1;
- reset();
-
- mPrevEvent = MotionEvent.obtain(event);
- mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1;
- mActiveId1 = event.getPointerId(event.getActionIndex());
- mActive0MostRecent = false;
-
- int index0 = event.findPointerIndex(mActiveId0);
- if (index0 < 0 || mActiveId0 == mActiveId1) {
- // Probably someone sending us a broken event stream.
- Log.e(TAG, "Got " + MotionEvent.actionToString(action) +
- " with bad state while a gesture was in progress. " +
- "Did you forget to pass an event to " +
- "ScaleGestureDetector#onTouchEvent?");
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_POINTER_UP: {
- final int pointerCount = event.getPointerCount();
- final int actionIndex = event.getActionIndex();
- final int actionId = event.getPointerId(actionIndex);
-
- boolean gestureEnded = false;
- if (pointerCount > 2) {
- if (actionId == mActiveId0) {
- final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId0 = event.getPointerId(newIndex);
- mActive0MostRecent = true;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- } else if (actionId == mActiveId1) {
- final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId1 = event.getPointerId(newIndex);
- mActive0MostRecent = false;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- }
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- } else {
- gestureEnded = true;
- }
-
- if (gestureEnded) {
- // Gesture ended
- setContext(event);
-
- // Set focus point to the remaining finger
- final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
- final int index = event.findPointerIndex(activeId);
- mFocusX = event.getX(index);
- mFocusY = event.getY(index);
-
- mListener.onScaleEnd(this);
- reset();
- mActiveId0 = activeId;
- mActive0MostRecent = true;
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- mListener.onScaleEnd(this);
- reset();
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE: {
- setContext(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit - this can help filter shaky data as a
- // finger is lifted.
- if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = mListener.onScale(this);
-
- if (updatePrevious) {
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- }
- }
- }
- break;
- }
- }
-
- if (!handled && mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
- return handled;
- }
-
- private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int removedPointerIndex) {
- final int pointerCount = ev.getPointerCount();
-
- // It's ok if this isn't found and returns -1, it simply won't match.
- final int otherActiveIndex = ev.findPointerIndex(otherActiveId);
-
- // Pick a new id and update tracking state.
- for (int i = 0; i < pointerCount; i++) {
- if (i != removedPointerIndex && i != otherActiveIndex) {
- return i;
- }
- }
- return -1;
- }
-
- private void setContext(MotionEvent curr) {
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- }
- mCurrEvent = MotionEvent.obtain(curr);
-
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
-
- final MotionEvent prev = mPrevEvent;
-
- final int prevIndex0 = prev.findPointerIndex(mActiveId0);
- final int prevIndex1 = prev.findPointerIndex(mActiveId1);
- final int currIndex0 = curr.findPointerIndex(mActiveId0);
- final int currIndex1 = curr.findPointerIndex(mActiveId1);
-
- if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
- mInvalidGesture = true;
- Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
- if (mGestureInProgress) {
+ final boolean streamComplete = action == MotionEvent.ACTION_UP ||
+ action == MotionEvent.ACTION_CANCEL;
+ if (action == MotionEvent.ACTION_DOWN || streamComplete) {
+ // Reset any scale in progress with the listener.
+ // If it's an ACTION_DOWN we're beginning a new event stream.
+ // This means the app probably didn't give us all the events. Shame on it.
+ if (mInProgress) {
mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = 0;
}
- return;
+
+ if (streamComplete) {
+ return true;
+ }
}
- final float px0 = prev.getX(prevIndex0);
- final float py0 = prev.getY(prevIndex0);
- final float px1 = prev.getX(prevIndex1);
- final float py1 = prev.getY(prevIndex1);
- final float cx0 = curr.getX(currIndex0);
- final float cy0 = curr.getY(currIndex0);
- final float cx1 = curr.getX(currIndex1);
- final float cy1 = curr.getY(currIndex1);
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? event.getActionIndex() : -1;
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- mFocusX = cx0 + cvx * 0.5f;
- mFocusY = cy0 + cvy * 0.5f;
- mTimeDelta = curr.getEventTime() - prev.getEventTime();
- mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1);
- mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1);
- }
-
- private void reset() {
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = event.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
}
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
+ // Determine average deviation from focal point
+ float devSumX = 0, devSumY = 0;
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ devSumX += Math.abs(event.getX(i) - focusX);
+ devSumY += Math.abs(event.getY(i) - focusY);
}
- mGestureInProgress = false;
- mActiveId0 = -1;
- mActiveId1 = -1;
- mInvalidGesture = false;
+ final float devX = devSumX / div;
+ final float devY = devSumY / div;
+
+ // Span is the average distance between touch points through the focal point;
+ // i.e. the diameter of the circle with a radius of the average deviation from
+ // the focal point.
+ final float spanX = devX * 2;
+ final float spanY = devY * 2;
+ final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+
+ // Dispatch begin/end events as needed.
+ // If the configuration changes, notify the app to reset its current state by beginning
+ // a fresh scale event stream.
+ final boolean wasInProgress = mInProgress;
+ mFocusX = focusX;
+ mFocusY = focusY;
+ if (mInProgress && (span == 0 || configChanged)) {
+ mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = span;
+ }
+ if (configChanged) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mInitialSpan = mPrevSpan = mCurrSpan = span;
+ }
+ if (!mInProgress && span != 0 &&
+ (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mPrevSpan = mCurrSpan = span;
+ mInProgress = mListener.onScaleBegin(this);
+ }
+
+ // Handle motion; focal point and span/scale factor are changing.
+ if (action == MotionEvent.ACTION_MOVE) {
+ mCurrSpanX = spanX;
+ mCurrSpanY = spanY;
+ mCurrSpan = span;
+
+ boolean updatePrev = true;
+ if (mInProgress) {
+ updatePrev = mListener.onScale(this);
+ }
+
+ if (updatePrev) {
+ mPrevSpanX = mCurrSpanX;
+ mPrevSpanY = mCurrSpanY;
+ mPrevSpan = mCurrSpan;
+ }
+ }
+
+ return true;
}
/**
- * Returns {@code true} if a two-finger scale gesture is in progress.
- * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ * Returns {@code true} if a scale gesture is in progress.
*/
public boolean isInProgress() {
- return mGestureInProgress;
+ return mInProgress;
}
/**
* Get the X coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -448,10 +290,9 @@
/**
* Get the Y coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -462,73 +303,63 @@
}
/**
- * Return the current distance between the two pointers forming the
- * gesture in progress.
+ * Return the average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
+ return mCurrSpan;
}
/**
- * Return the current x distance between the two pointers forming the
- * gesture in progress.
+ * Return the average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanX() {
- return mCurrFingerDiffX;
+ return mCurrSpanX;
}
/**
- * Return the current y distance between the two pointers forming the
- * gesture in progress.
+ * Return the average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanY() {
- return mCurrFingerDiffY;
+ return mCurrSpanY;
}
/**
- * Return the previous distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
+ return mPrevSpan;
}
/**
- * Return the previous x distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanX() {
- return mPrevFingerDiffX;
+ return mPrevSpanX;
}
/**
- * Return the previous y distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanY() {
- return mPrevFingerDiffY;
+ return mPrevSpanY;
}
/**
@@ -539,10 +370,7 @@
* @return The current scaling factor.
*/
public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
+ return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
/**
@@ -552,7 +380,7 @@
* @return Time difference since the last scaling event in milliseconds.
*/
public long getTimeDelta() {
- return mTimeDelta;
+ return mCurrTime - mPrevTime;
}
/**
@@ -561,6 +389,6 @@
* @return Current event time in milliseconds.
*/
public long getEventTime() {
- return mCurrEvent.getEventTime();
+ return mCurrTime;
}
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index a6d1a3f..db05e10 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -16,8 +16,16 @@
package android.view;
+import dalvik.system.CloseGuard;
+
import android.content.res.CompatibilityInfo.Translator;
-import android.graphics.*;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.SurfaceTexture;
+import android.os.IBinder;
import android.os.Parcelable;
import android.os.Parcel;
import android.os.SystemProperties;
@@ -27,206 +35,187 @@
* Handle onto a raw buffer that is being managed by the screen compositor.
*/
public class Surface implements Parcelable {
- private static final String LOG_TAG = "Surface";
- private static final boolean DEBUG_RELEASE = false;
-
- /* orientations for setOrientation() */
- public static final int ROTATION_0 = 0;
- public static final int ROTATION_90 = 1;
- public static final int ROTATION_180 = 2;
- public static final int ROTATION_270 = 3;
+ private static final String TAG = "Surface";
- private static final boolean headless = "1".equals(
+ private static final boolean HEADLESS = "1".equals(
SystemProperties.get("ro.config.headless", "0"));
- private static void checkHeadless() {
- if(headless) {
- throw new UnsupportedOperationException("Device is headless");
+ public static final Parcelable.Creator<Surface> CREATOR =
+ new Parcelable.Creator<Surface>() {
+ public Surface createFromParcel(Parcel source) {
+ try {
+ Surface s = new Surface();
+ s.readFromParcel(source);
+ return s;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating surface from parcel", e);
+ return null;
+ }
}
- }
+
+ public Surface[] newArray(int size) {
+ return new Surface[size];
+ }
+ };
/**
- * Create Surface from a {@link SurfaceTexture}.
- *
- * Images drawn to the Surface will be made available to the {@link
- * SurfaceTexture}, which can attach them an OpenGL ES texture via {@link
- * SurfaceTexture#updateTexImage}.
- *
- * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
- * Surface.
+ * Rotation constant: 0 degree rotation (natural orientation)
*/
- public Surface(SurfaceTexture surfaceTexture) {
- checkHeadless();
-
- if (DEBUG_RELEASE) {
- mCreationStack = new Exception();
- }
- mCanvas = new CompatibleCanvas();
- initFromSurfaceTexture(surfaceTexture);
- }
+ public static final int ROTATION_0 = 0;
/**
- * Does this object hold a valid surface? Returns true if it holds
- * a physical surface, so lockCanvas() will succeed. Otherwise
- * returns false.
+ * Rotation constant: 90 degree rotation.
*/
- public native boolean isValid();
-
- /** Release the local reference to the server-side surface.
- * Always call release() when you're done with a Surface. This will
- * make the surface invalid.
- */
- public native void release();
-
- /** draw into a surface */
- public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException, IllegalArgumentException {
- /*
- * the dirty rectangle may be expanded to the surface's size, if for
- * instance it has been resized or if the bits were lost, since the last
- * call.
- */
- return lockCanvasNative(dirty);
- }
-
- /** unlock the surface and asks a page flip */
- public native void unlockCanvasAndPost(Canvas canvas);
-
- /**
- * unlock the surface. the screen won't be updated until
- * post() or postAll() is called
- */
- public native void unlockCanvas(Canvas canvas);
-
- @Override
- public String toString() {
- return "Surface(name=" + mName + ", identity=" + getIdentity() + ")";
- }
-
- public int describeContents() {
- return 0;
- }
-
- public native void readFromParcel(Parcel source);
- public native void writeToParcel(Parcel dest, int flags);
+ public static final int ROTATION_90 = 1;
/**
- * Exception thrown when a surface couldn't be created or resized
+ * Rotation constant: 180 degree rotation.
*/
- public static class OutOfResourcesException extends Exception {
- public OutOfResourcesException() {
- }
- public OutOfResourcesException(String name) {
- super(name);
- }
- }
-
- /*
- * -----------------------------------------------------------------------
- * No user serviceable parts beyond this point
- * -----------------------------------------------------------------------
+ public static final int ROTATION_180 = 2;
+
+ /**
+ * Rotation constant: 270 degree rotation.
*/
+ public static final int ROTATION_270 = 3;
- /* flags used in constructor (keep in sync with ISurfaceComposer.h) */
+ /* built-in physical display ids (keep in sync with ISurfaceComposer.h)
+ * these are different from the logical display ids used elsewhere in the framework */
- /** Surface is created hidden @hide */
- public static final int HIDDEN = 0x00000004;
+ /**
+ * Built-in physical display id: Main display.
+ * Use only with {@link #getBuiltInDisplay()}.
+ * @hide
+ */
+ public static final int BUILT_IN_DISPLAY_ID_MAIN = 0;
- /** The surface contains secure content, special measures will
- * be taken to disallow the surface's content to be copied from
- * another process. In particular, screenshots and VNC servers will
+ /**
+ * Built-in physical display id: Attached HDMI display.
+ * Use only with {@link #getBuiltInDisplay()}.
+ * @hide
+ */
+ public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
+
+ /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
+
+ /**
+ * Surface creation flag: Surface is created hidden
+ * @hide */
+ public static final int HIDDEN = 0x00000004;
+
+ /**
+ * Surface creation flag: The surface contains secure content, special
+ * measures will be taken to disallow the surface's content to be copied
+ * from another process. In particular, screenshots and VNC servers will
* be disabled, but other measures can take place, for instance the
* surface might not be hardware accelerated.
- * @hide*/
- public static final int SECURE = 0x00000080;
-
- /** Creates a surface where color components are interpreted as
- * "non pre-multiplied" by their alpha channel. Of course this flag is
- * meaningless for surfaces without an alpha channel. By default
- * surfaces are pre-multiplied, which means that each color component is
- * already multiplied by its alpha value. In this case the blending
- * equation used is:
- *
- * DEST = SRC + DEST * (1-SRC_ALPHA)
- *
- * By contrast, non pre-multiplied surfaces use the following equation:
- *
- * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA)
- *
- * pre-multiplied surfaces must always be used if transparent pixels are
- * composited on top of each-other into the surface. A pre-multiplied
- * surface can never lower the value of the alpha component of a given
- * pixel.
- *
- * In some rare situations, a non pre-multiplied surface is preferable.
- *
- * @hide
+ * @hide
*/
- public static final int NON_PREMULTIPLIED = 0x00000100;
-
+ public static final int SECURE = 0x00000080;
+
/**
- * Indicates that the surface must be considered opaque, even if its
- * pixel format is set to translucent. This can be useful if an
+ * Surface creation flag: Creates a surface where color components are interpreted
+ * as "non pre-multiplied" by their alpha channel. Of course this flag is
+ * meaningless for surfaces without an alpha channel. By default
+ * surfaces are pre-multiplied, which means that each color component is
+ * already multiplied by its alpha value. In this case the blending
+ * equation used is:
+ *
+ * DEST = SRC + DEST * (1-SRC_ALPHA)
+ *
+ * By contrast, non pre-multiplied surfaces use the following equation:
+ *
+ * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA)
+ *
+ * pre-multiplied surfaces must always be used if transparent pixels are
+ * composited on top of each-other into the surface. A pre-multiplied
+ * surface can never lower the value of the alpha component of a given
+ * pixel.
+ *
+ * In some rare situations, a non pre-multiplied surface is preferable.
+ * @hide
+ */
+ public static final int NON_PREMULTIPLIED = 0x00000100;
+
+ /**
+ * Surface creation flag: Indicates that the surface must be considered opaque,
+ * even if its pixel format is set to translucent. This can be useful if an
* application needs full RGBA 8888 support for instance but will
* still draw every pixel opaque.
- *
* @hide
*/
- public static final int OPAQUE = 0x00000400;
-
+ public static final int OPAQUE = 0x00000400;
+
/**
- * Application requires a hardware-protected path to an
+ * Surface creation flag: Application requires a hardware-protected path to an
* external display sink. If a hardware-protected path is not available,
* then this surface will not be displayed on the external sink.
- *
* @hide
*/
- public static final int PROTECTED_APP = 0x00000800;
+ public static final int PROTECTED_APP = 0x00000800;
// 0x1000 is reserved for an independent DRM protected flag in framework
- /** Creates a normal surface. This is the default. @hide */
+ /**
+ * Surface creation flag: Creates a normal surface.
+ * This is the default.
+ * @hide
+ */
public static final int FX_SURFACE_NORMAL = 0x00000000;
-
- /** Creates a Blur surface. Everything behind this surface is blurred
- * by some amount. The quality and refresh speed of the blur effect
- * is not settable or guaranteed.
- * It is an error to lock a Blur surface, since it doesn't have
- * a backing store.
+
+ /**
+ * Surface creation flag: Creates a Blur surface.
+ * Everything behind this surface is blurred by some amount.
+ * The quality and refresh speed of the blur effect is not settable or guaranteed.
+ * It is an error to lock a Blur surface, since it doesn't have a backing store.
* @hide
* @deprecated
*/
@Deprecated
- public static final int FX_SURFACE_BLUR = 0x00010000;
-
- /** Creates a Dim surface. Everything behind this surface is dimmed
- * by the amount specified in {@link #setAlpha}.
- * It is an error to lock a Dim surface, since it doesn't have
- * a backing store.
+ public static final int FX_SURFACE_BLUR = 0x00010000;
+
+ /**
+ * Surface creation flag: Creates a Dim surface.
+ * Everything behind this surface is dimmed by the amount specified
+ * in {@link #setAlpha}. It is an error to lock a Dim surface, since it
+ * doesn't have a backing store.
* @hide
*/
- public static final int FX_SURFACE_DIM = 0x00020000;
+ public static final int FX_SURFACE_DIM = 0x00020000;
- /** @hide */
- public static final int FX_SURFACE_SCREENSHOT = 0x00030000;
+ /**
+ * @hide
+ */
+ public static final int FX_SURFACE_SCREENSHOT = 0x00030000;
- /** Mask used for FX values above @hide */
- public static final int FX_SURFACE_MASK = 0x000F0000;
+ /**
+ * Mask used for FX values above.
+ * @hide
+ */
+ public static final int FX_SURFACE_MASK = 0x000F0000;
/* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */
- /** Hide the surface. Equivalent to calling hide(). @hide */
- public static final int SURFACE_HIDDEN = 0x01;
+ /**
+ * Surface flag: Hide the surface.
+ * Equivalent to calling hide().
+ * @hide
+ */
+ public static final int SURFACE_HIDDEN = 0x01;
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private String mName;
+
+ // Note: These fields are accessed by native code.
// The mSurfaceControl will only be present for Surfaces used by the window
// server or system processes. When this class is parceled we defer to the
// mSurfaceControl to do the parceling. Otherwise we parcel the
// mNativeSurface.
- private int mSurfaceControl;
- private int mSaveCount;
- private Canvas mCanvas;
- private int mNativeSurface;
- private int mSurfaceGenerationId;
- private String mName;
+ private int mNativeSurface; // Surface*
+ private int mNativeSurfaceControl; // SurfaceControl*
+ private int mGenerationId; // incremented each time mNativeSurface changes
+ private final Canvas mCanvas = new CompatibleCanvas();
+ private int mCanvasSaveCount; // Canvas save count at time of lockCanvas()
// The Translator for density compatibility mode. This is used for scaling
// the canvas to perform the appropriate density transformation.
@@ -236,46 +225,417 @@
// non compatibility mode.
private Matrix mCompatibleMatrix;
- private Exception mCreationStack;
+ private native void nativeCreate(SurfaceSession session, String name,
+ int w, int h, int format, int flags)
+ throws OutOfResourcesException;
+ private native void nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
+ throws OutOfResourcesException;
+ private native void nativeRelease();
+ private native void nativeDestroy();
+ private native boolean nativeIsValid();
+ private native int nativeGetIdentity();
+ private native boolean nativeIsConsumerRunningBehind();
- /*
- * We use a class initializer to allow the native code to cache some
- * field offsets.
- */
- native private static void nativeClassInit();
- static { nativeClassInit(); }
+ private native Canvas nativeLockCanvas(Rect dirty);
+ private native void nativeUnlockCanvasAndPost(Canvas canvas);
- /** create a surface with a name @hide */
- public Surface(SurfaceSession s,
- int pid, String name, int layerStack, int w, int h, int format, int flags)
- throws OutOfResourcesException {
- checkHeadless();
+ private static native Bitmap nativeScreenshot(IBinder displayToken,
+ int width, int height, int minLayer, int maxLayer, boolean allLayers);
- if (DEBUG_RELEASE) {
- mCreationStack = new Exception();
- }
- mCanvas = new CompatibleCanvas();
- init(s, pid, name, layerStack, w, h, format, flags);
- mName = name;
- }
+ private static native void nativeOpenTransaction();
+ private static native void nativeCloseTransaction();
+
+ private native void nativeSetLayer(int zorder);
+ private native void nativeSetPosition(float x, float y);
+ private native void nativeSetSize(int w, int h);
+ private native void nativeSetTransparentRegionHint(Region region);
+ private native void nativeSetAlpha(float alpha);
+ private native void nativeSetMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
+ private native void nativeSetFlags(int flags, int mask);
+ private native void nativeSetWindowCrop(Rect crop);
+ private native void nativeSetLayerStack(int layerStack);
+
+ private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
+ private static native IBinder nativeCreateDisplay(String name);
+ private static native void nativeSetDisplaySurface(
+ IBinder displayToken, SurfaceTexture surfaceTexture);
+ private static native void nativeSetDisplayLayerStack(
+ IBinder displayToken, int layerStack);
+ private static native void nativeSetDisplayProjection(
+ IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect);
+ private static native boolean nativeGetDisplayInfo(
+ IBinder displayToken, PhysicalDisplayInfo outInfo);
+
+ private native void nativeCopyFrom(Surface other);
+ private native void nativeTransferFrom(Surface other);
+ private native void nativeReadFromParcel(Parcel source);
+ private native void nativeWriteToParcel(Parcel dest);
+
/**
- * Create an empty surface, which will later be filled in by
- * readFromParcel().
+ * Create an empty surface, which will later be filled in by readFromParcel().
* @hide
*/
public Surface() {
checkHeadless();
- if (DEBUG_RELEASE) {
- mCreationStack = new Exception();
- }
- mCanvas = new CompatibleCanvas();
+ mCloseGuard.open("release");
}
- private Surface(Parcel source) throws OutOfResourcesException {
- init(source);
+ /**
+ * Create a surface with a name.
+ *
+ * The surface creation flags specify what kind of surface to create and
+ * certain options such as whether the surface can be assumed to be opaque
+ * and whether it should be initially hidden. Surfaces should always be
+ * created with the {@link #HIDDEN} flag set to ensure that they are not
+ * made visible prematurely before all of the surface's properties have been
+ * configured.
+ *
+ * Good practice is to first create the surface with the {@link #HIDDEN} flag
+ * specified, open a transaction, set the surface layer, layer stack, alpha,
+ * and position, call {@link #show} if appropriate, and close the transaction.
+ *
+ * @param session The surface session, must not be null.
+ * @param name The surface name, must not be null.
+ * @param w The surface initial width.
+ * @param h The surface initial height.
+ * @param flags The surface creation flags. Should always include {@link #HIDDEN}
+ * in the creation flags.
+ * @hide
+ */
+ public Surface(SurfaceSession session,
+ String name, int w, int h, int format, int flags)
+ throws OutOfResourcesException {
+ if (session == null) {
+ throw new IllegalArgumentException("session must not be null");
+ }
+ if (name == null) {
+ throw new IllegalArgumentException("name must not be null");
+ }
+
+ if ((flags & HIDDEN) == 0) {
+ Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set "
+ + "to ensure that they are not made visible prematurely before "
+ + "all of the surface's properties have been configured. "
+ + "Set the other properties and make the surface visible within "
+ + "a transaction. New surface name: " + name,
+ new Throwable());
+ }
+
+ checkHeadless();
+
+ mName = name;
+ nativeCreate(session, name, w, h, format, flags);
+
+ mCloseGuard.open("release");
+ }
+
+ /**
+ * Create Surface from a {@link SurfaceTexture}.
+ *
+ * Images drawn to the Surface will be made available to the {@link
+ * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
+ * SurfaceTexture#updateTexImage}.
+ *
+ * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
+ * Surface.
+ */
+ public Surface(SurfaceTexture surfaceTexture) {
+ if (surfaceTexture == null) {
+ throw new IllegalArgumentException("surfaceTexture must not be null");
+ }
+
+ checkHeadless();
+
+ mName = surfaceTexture.toString();
+ try {
+ nativeCreateFromSurfaceTexture(surfaceTexture);
+ } catch (OutOfResourcesException ex) {
+ // We can't throw OutOfResourcesException because it would be an API change.
+ throw new RuntimeException(ex);
+ }
+
+ mCloseGuard.open("release");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ nativeRelease();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Release the local reference to the server-side surface.
+ * Always call release() when you're done with a Surface.
+ * This will make the surface invalid.
+ */
+ public void release() {
+ nativeRelease();
+ mCloseGuard.close();
+ }
+
+ /**
+ * Free all server-side state associated with this surface and
+ * release this object's reference. This method can only be
+ * called from the process that created the service.
+ * @hide
+ */
+ public void destroy() {
+ nativeDestroy();
+ mCloseGuard.close();
+ }
+
+ /**
+ * Returns true if this object holds a valid surface.
+ *
+ * @return True if it holds a physical surface, so lockCanvas() will succeed.
+ * Otherwise returns false.
+ */
+ public boolean isValid() {
+ return nativeIsValid();
+ }
+
+ /**
+ * Gets the generation number of this surface, incremented each time
+ * the native surface contained within this object changes.
+ *
+ * @return The current generation number.
+ * @hide
+ */
+ public int getGenerationId() {
+ return mGenerationId;
+ }
+
+ /**
+ * Returns true if the consumer of this Surface is running behind the producer.
+ *
+ * @return True if the consumer is more than one buffer ahead of the producer.
+ * @hide
+ */
+ public boolean isConsumerRunningBehind() {
+ return nativeIsConsumerRunningBehind();
+ }
+
+ /**
+ * Gets a {@link Canvas} for drawing into this surface.
+ *
+ * After drawing into the provided {@link Canvas}, the caller should
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * @param dirty A rectangle that represents the dirty region that the caller wants
+ * to redraw. This function may choose to expand the dirty rectangle if for example
+ * the surface has been resized or if the previous contents of the surface were
+ * not available. The caller should redraw the entire dirty region as represented
+ * by the contents of the dirty rect upon return from this function.
+ * The caller may also pass <code>null</code> instead, in the case where the
+ * entire surface should be redrawn.
+ * @return A canvas for drawing into the surface.
+ */
+ public Canvas lockCanvas(Rect dirty)
+ throws OutOfResourcesException, IllegalArgumentException {
+ return nativeLockCanvas(dirty);
+ }
+
+ /**
+ * Posts the new contents of the {@link Canvas} to the surface and
+ * releases the {@link Canvas}.
+ *
+ * @param canvas The canvas previously obtained from {@link #lockCanvas}.
+ */
+ public void unlockCanvasAndPost(Canvas canvas) {
+ nativeUnlockCanvasAndPost(canvas);
+ }
+
+ /**
+ * @deprecated This API has been removed and is not supported. Do not use.
+ */
+ @Deprecated
+ public void unlockCanvas(Canvas canvas) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets the translator used to scale canvas's width/height in compatibility
+ * mode.
+ */
+ void setCompatibilityTranslator(Translator translator) {
+ if (translator != null) {
+ float appScale = translator.applicationScale;
+ mCompatibleMatrix = new Matrix();
+ mCompatibleMatrix.setScale(appScale, appScale);
+ }
+ }
+
+ /**
+ * Like {@link #screenshot(int, int, int, int)} but includes all
+ * Surfaces in the screenshot.
+ *
+ * @hide
+ */
+ public static Bitmap screenshot(int width, int height) {
+ // TODO: should take the display as a parameter
+ IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN);
+ return nativeScreenshot(displayToken, width, height, 0, 0, true);
+ }
+
+ /**
+ * Copy the current screen contents into a bitmap and return it.
+ *
+ * @param width The desired width of the returned bitmap; the raw
+ * screen will be scaled down to this size.
+ * @param height The desired height of the returned bitmap; the raw
+ * screen will be scaled down to this size.
+ * @param minLayer The lowest (bottom-most Z order) surface layer to
+ * include in the screenshot.
+ * @param maxLayer The highest (top-most Z order) surface layer to
+ * include in the screenshot.
+ * @return Returns a Bitmap containing the screen contents, or null
+ * if an error occurs.
+ *
+ * @hide
+ */
+ public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) {
+ // TODO: should take the display as a parameter
+ IBinder displayToken = getBuiltInDisplay(BUILT_IN_DISPLAY_ID_MAIN);
+ return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false);
+ }
+
+ /*
+ * set surface parameters.
+ * needs to be inside open/closeTransaction block
+ */
+
+ /** start a transaction @hide */
+ public static void openTransaction() {
+ nativeOpenTransaction();
+ }
+
+ /** end a transaction @hide */
+ public static void closeTransaction() {
+ nativeCloseTransaction();
+ }
+
+ /** @hide */
+ public void setLayer(int zorder) {
+ nativeSetLayer(zorder);
+ }
+
+ /** @hide */
+ public void setPosition(int x, int y) {
+ nativeSetPosition((float)x, (float)y);
+ }
+
+ /** @hide */
+ public void setPosition(float x, float y) {
+ nativeSetPosition(x, y);
+ }
+
+ /** @hide */
+ public void setSize(int w, int h) {
+ nativeSetSize(w, h);
+ }
+
+ /** @hide */
+ public void hide() {
+ nativeSetFlags(SURFACE_HIDDEN, SURFACE_HIDDEN);
+ }
+
+ /** @hide */
+ public void show() {
+ nativeSetFlags(0, SURFACE_HIDDEN);
+ }
+
+ /** @hide */
+ public void setTransparentRegionHint(Region region) {
+ nativeSetTransparentRegionHint(region);
+ }
+
+ /** @hide */
+ public void setAlpha(float alpha) {
+ nativeSetAlpha(alpha);
+ }
+
+ /** @hide */
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ nativeSetMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ /** @hide */
+ public void setFlags(int flags, int mask) {
+ nativeSetFlags(flags, mask);
+ }
+
+ /** @hide */
+ public void setWindowCrop(Rect crop) {
+ nativeSetWindowCrop(crop);
+ }
+
+ /** @hide */
+ public void setLayerStack(int layerStack) {
+ nativeSetLayerStack(layerStack);
+ }
+
+ /** @hide */
+ public static IBinder getBuiltInDisplay(int builtInDisplayId) {
+ return nativeGetBuiltInDisplay(builtInDisplayId);
+ }
+
+ /** @hide */
+ public static IBinder createDisplay(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name must not be null");
+ }
+ return nativeCreateDisplay(name);
+ }
+
+ /** @hide */
+ public static void setDisplaySurface(IBinder displayToken, SurfaceTexture surfaceTexture) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ nativeSetDisplaySurface(displayToken, surfaceTexture);
+ }
+
+ /** @hide */
+ public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ nativeSetDisplayLayerStack(displayToken, layerStack);
+ }
+
+ /** @hide */
+ public static void setDisplayProjection(IBinder displayToken,
+ int orientation, Rect layerStackRect, Rect displayRect) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (layerStackRect == null) {
+ throw new IllegalArgumentException("layerStackRect must not be null");
+ }
+ if (displayRect == null) {
+ throw new IllegalArgumentException("displayRect must not be null");
+ }
+ nativeSetDisplayProjection(displayToken, orientation, layerStackRect, displayRect);
+ }
+
+ /** @hide */
+ public static boolean getDisplayInfo(IBinder displayToken, PhysicalDisplayInfo outInfo) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (outInfo == null) {
+ throw new IllegalArgumentException("outInfo must not be null");
+ }
+ return nativeGetDisplayInfo(displayToken, outInfo);
}
/**
@@ -287,35 +647,141 @@
* in to it.
* @hide
*/
- public native void copyFrom(Surface o);
+ public void copyFrom(Surface other) {
+ if (other == null) {
+ throw new IllegalArgumentException("other must not be null");
+ }
+ if (other != this) {
+ nativeCopyFrom(other);
+ }
+ }
/**
- * Transfer the native state from 'o' to this surface, releasing it
- * from 'o'. This is for use in the client side for drawing into a
+ * Transfer the native state from 'other' to this surface, releasing it
+ * from 'other'. This is for use in the client side for drawing into a
* surface; not guaranteed to work on the window manager side.
* This is for use by the client to move the underlying surface from
* one Surface object to another, in particular in SurfaceFlinger.
* @hide.
*/
- public native void transferFrom(Surface o);
-
- /** @hide */
- public int getGenerationId() {
- return mSurfaceGenerationId;
+ public void transferFrom(Surface other) {
+ if (other == null) {
+ throw new IllegalArgumentException("other must not be null");
+ }
+ if (other != this) {
+ nativeTransferFrom(other);
+ }
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public void readFromParcel(Parcel source) {
+ if (source == null) {
+ throw new IllegalArgumentException("source must not be null");
+ }
+
+ mName = source.readString();
+ nativeReadFromParcel(source);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (dest == null) {
+ throw new IllegalArgumentException("dest must not be null");
+ }
+
+ dest.writeString(mName);
+ nativeWriteToParcel(dest);
+ if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+ release();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Surface(name=" + mName + ", identity=" + nativeGetIdentity() + ")";
+ }
+
+ private static void checkHeadless() {
+ if (HEADLESS) {
+ throw new UnsupportedOperationException("Device is headless");
+ }
+ }
/**
- * Whether the consumer of this Surface is running behind the producer;
- * that is, isConsumerRunningBehind() returns true if the consumer is more
- * than one buffer ahead of the producer.
+ * Exception thrown when a surface couldn't be created or resized.
+ */
+ public static class OutOfResourcesException extends Exception {
+ public OutOfResourcesException() {
+ }
+
+ public OutOfResourcesException(String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Describes the properties of a physical display known to surface flinger.
* @hide
*/
- public native boolean isConsumerRunningBehind();
+ public static final class PhysicalDisplayInfo {
+ public int width;
+ public int height;
+ public float refreshRate;
+ public float density;
+ public float xDpi;
+ public float yDpi;
+
+ public PhysicalDisplayInfo() {
+ }
+
+ public PhysicalDisplayInfo(PhysicalDisplayInfo other) {
+ copyFrom(other);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o);
+ }
+
+ public boolean equals(PhysicalDisplayInfo other) {
+ return other != null
+ && width == other.width
+ && height == other.height
+ && refreshRate == other.refreshRate
+ && density == other.density
+ && xDpi == other.xDpi
+ && yDpi == other.yDpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
+ public void copyFrom(PhysicalDisplayInfo other) {
+ width = other.width;
+ height = other.height;
+ refreshRate = other.refreshRate;
+ density = other.density;
+ xDpi = other.xDpi;
+ yDpi = other.yDpi;
+ }
+
+ // For debugging purposes
+ @Override
+ public String toString() {
+ return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ + "density " + density + ", " + xDpi + " x " + yDpi + " dpi}";
+ }
+ }
/**
- * A Canvas class that can handle the compatibility mode. This does two
- * things differently.
+ * A Canvas class that can handle the compatibility mode.
+ * This does two things differently.
* <ul>
* <li>Returns the width and height of the target metrics, rather than
* native. For example, the canvas returns 320x480 even if an app is running
@@ -328,7 +794,7 @@
* that this model does not work, but we hope this works for many apps.
* </ul>
*/
- private class CompatibleCanvas extends Canvas {
+ private final class CompatibleCanvas extends Canvas {
// A temp matrix to remember what an application obtained via {@link getMatrix}
private Matrix mOrigMatrix = null;
@@ -372,137 +838,4 @@
mOrigMatrix.set(m);
}
}
-
- /**
- * Sets the translator used to scale canvas's width/height in compatibility
- * mode.
- */
- void setCompatibilityTranslator(Translator translator) {
- if (translator != null) {
- float appScale = translator.applicationScale;
- mCompatibleMatrix = new Matrix();
- mCompatibleMatrix.setScale(appScale, appScale);
- }
- }
-
- /** Free all server-side state associated with this surface and
- * release this object's reference. @hide */
- public native void destroy();
-
- private native Canvas lockCanvasNative(Rect dirty) throws OutOfResourcesException;
-
- /**
- * set the orientation of the given display.
- * @param display
- * @param orientation
- * @hide
- */
- public static native void setOrientation(int display, int orientation);
-
- /**
- * Like {@link #screenshot(int, int, int, int)} but includes all
- * Surfaces in the screenshot.
- *
- * @hide
- */
- public static native Bitmap screenshot(int width, int height);
-
- /**
- * Copy the current screen contents into a bitmap and return it.
- *
- * @param width The desired width of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param height The desired height of the returned bitmap; the raw
- * screen will be scaled down to this size.
- * @param minLayer The lowest (bottom-most Z order) surface layer to
- * include in the screenshot.
- * @param maxLayer The highest (top-most Z order) surface layer to
- * include in the screenshot.
- * @return Returns a Bitmap containing the screen contents.
- *
- * @hide
- */
- public static native Bitmap screenshot(int width, int height, int minLayer, int maxLayer);
-
-
- /*
- * set surface parameters.
- * needs to be inside open/closeTransaction block
- */
-
- /** start a transaction @hide */
- public static native void openTransaction();
- /** end a transaction @hide */
- public static native void closeTransaction();
- /** @hide */
- public native void setLayer(int zorder);
- /** @hide */
- public void setPosition(int x, int y) { setPosition((float)x, (float)y); }
- /** @hide */
- public native void setPosition(float x, float y);
- /** @hide */
- public native void setSize(int w, int h);
- /** @hide */
- public native void hide();
- /** @hide */
- public native void show();
- /** @hide */
- public native void setTransparentRegionHint(Region region);
- /** @hide */
- public native void setAlpha(float alpha);
- /** @hide */
- public native void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
- /** @hide */
- public native void setFlags(int flags, int mask);
- /** @hide */
- public native void setWindowCrop(Rect crop);
- /** @hide */
- public native void setLayerStack(int layerStack);
-
-
-
- public static final Parcelable.Creator<Surface> CREATOR
- = new Parcelable.Creator<Surface>()
- {
- public Surface createFromParcel(Parcel source) {
- try {
- return new Surface(source);
- } catch (Exception e) {
- Log.e(LOG_TAG, "Exception creating surface from parcel", e);
- }
- return null;
- }
-
- public Surface[] newArray(int size) {
- return new Surface[size];
- }
- };
-
- @Override
- protected void finalize() throws Throwable {
- try {
- super.finalize();
- } finally {
- if (mNativeSurface != 0 || mSurfaceControl != 0) {
- if (DEBUG_RELEASE) {
- Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
- + mNativeSurface + ", " + mSurfaceControl + ")", mCreationStack);
- } else {
- Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() ("
- + mNativeSurface + ", " + mSurfaceControl + ")");
- }
- }
- release();
- }
- }
-
- private native void init(SurfaceSession s,
- int pid, String name, int layerStack, int w, int h, int format, int flags)
- throws OutOfResourcesException;
-
- private native void init(Parcel source) throws OutOfResourcesException;
-
- private native void initFromSurfaceTexture(SurfaceTexture surfaceTexture);
-
- private native int getIdentity();
}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index 2a04675..0dfd94a 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -16,34 +16,44 @@
package android.view;
-
/**
* An instance of this class represents a connection to the surface
- * flinger, in which you can create one or more Surface instances that will
+ * flinger, from which you can create one or more Surface instances that will
* be composited to the screen.
* {@hide}
*/
-public class SurfaceSession {
+public final class SurfaceSession {
+ // Note: This field is accessed by native code.
+ private int mNativeClient; // SurfaceComposerClient*
+
+ private static native int nativeCreate();
+ private static native void nativeDestroy(int ptr);
+ private static native void nativeKill(int ptr);
+
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
- init();
+ mNativeClient = nativeCreate();
}
- /** Forcibly detach native resources associated with this object.
- * Unlike destroy(), after this call any surfaces that were created
- * from the session will no longer work. The session itself is destroyed.
- */
- public native void kill();
-
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
- destroy();
+ try {
+ if (mNativeClient != 0) {
+ nativeDestroy(mNativeClient);
+ }
+ } finally {
+ super.finalize();
+ }
}
-
- private native void init();
- private native void destroy();
-
- private int mClient;
+
+ /**
+ * Forcibly detach native resources associated with this object.
+ * Unlike destroy(), after this call any surfaces that were created
+ * from the session will no longer work.
+ */
+ public void kill() {
+ nativeKill(mNativeClient);
+ }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fdf1c22..973c7f6 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -615,21 +615,22 @@
mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
}
- public void resized(int w, int h, Rect contentInsets,
+ @Override
+ public void resized(Rect frame, Rect contentInsets,
Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
SurfaceView surfaceView = mSurfaceView.get();
if (surfaceView != null) {
if (DEBUG) Log.v(
- "SurfaceView", surfaceView + " got resized: w=" +
- w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+ "SurfaceView", surfaceView + " got resized: w=" + frame.width()
+ + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
surfaceView.mSurfaceLock.lock();
try {
if (reportDraw) {
surfaceView.mUpdateWindowNeeded = true;
surfaceView.mReportDrawNeeded = true;
surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
- } else if (surfaceView.mWinFrame.width() != w
- || surfaceView.mWinFrame.height() != h) {
+ } else if (surfaceView.mWinFrame.width() != frame.width()
+ || surfaceView.mWinFrame.height() != frame.height()) {
surfaceView.mUpdateWindowNeeded = true;
surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b1f5e9e..d268fd2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -4925,6 +4926,18 @@
}
/**
+ * Returns the delegate for implementing accessibility support via
+ * composition. For more details see {@link AccessibilityDelegate}.
+ *
+ * @return The delegate, or null if none set.
+ *
+ * @hide
+ */
+ public AccessibilityDelegate getAccessibilityDelegate() {
+ return mAccessibilityDelegate;
+ }
+
+ /**
* Sets a delegate for implementing accessibility support via compositon as
* opposed to inheritance. The delegate's primary use is for implementing
* backwards compatible widgets. For more details see {@link AccessibilityDelegate}.
@@ -6246,6 +6259,11 @@
if (viewRootImpl != null) {
viewRootImpl.setAccessibilityFocus(this, null);
}
+ if (mAttachInfo != null) {
+ Rect rectangle = mAttachInfo.mTmpInvalRect;
+ getDrawingRect(rectangle);
+ requestRectangleOnScreen(rectangle);
+ }
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
notifyAccessibilityStateChanged();
@@ -6792,7 +6810,7 @@
* @hide
*/
public CharSequence getIterableTextForAccessibility() {
- return mContentDescription;
+ return getContentDescription();
}
/**
@@ -7347,7 +7365,9 @@
outRect.bottom -= insets.bottom;
return;
}
- Display d = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ // The view is not attached to a display so we don't have a context.
+ // Make a best guess about the display size.
+ Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
d.getRectSize(outRect);
}
@@ -11344,6 +11364,7 @@
resolveLayoutParams();
resolveTextDirection();
resolveTextAlignment();
+ resolveDrawables();
}
/**
@@ -12522,10 +12543,15 @@
final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
- if (width <= 0 || height <= 0 ||
- // Projected bitmap size in bytes
- (width * height * (opaque && !use32BitCache ? 2 : 4) >
- ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
+ final int projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
+ final int drawingCacheSize =
+ ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
+ if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
+ if (width > 0 && height > 0) {
+ Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
+ + projectedBitmapSize + " bytes, only "
+ + drawingCacheSize + " available");
+ }
destroyDrawingCache();
mCachingFailed = true;
return;
@@ -13040,7 +13066,9 @@
// to call invalidate() successfully when doing animations
mPrivateFlags |= PFLAG_DRAWN;
- if (!concatMatrix && (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == 0 &&
+ if (!concatMatrix &&
+ (flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
+ ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
@@ -14090,7 +14118,7 @@
@RemotableViewMethod
public void setBackgroundColor(int color) {
if (mBackground instanceof ColorDrawable) {
- ((ColorDrawable) mBackground).setColor(color);
+ ((ColorDrawable) mBackground.mutate()).setColor(color);
computeOpaqueFlags();
} else {
setBackground(new ColorDrawable(color));
@@ -17610,23 +17638,27 @@
// use use a height of 1, and then wack the matrix each time we
// actually use it.
shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
-
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+
this.host = host;
}
public void setFadeColor(int color) {
- if (color != 0 && color != mLastColor) {
+ if (color != mLastColor) {
mLastColor = color;
- color |= 0xFF000000;
- shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000,
- color & 0x00FFFFFF, Shader.TileMode.CLAMP);
-
- paint.setShader(shader);
- // Restore the default transfer mode (src_over)
- paint.setXfermode(null);
+ if (color != 0) {
+ shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000,
+ color & 0x00FFFFFF, Shader.TileMode.CLAMP);
+ paint.setShader(shader);
+ // Restore the default transfer mode (src_over)
+ paint.setXfermode(null);
+ } else {
+ shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
+ paint.setShader(shader);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ }
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3ab0e94..62e1383 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3806,6 +3806,13 @@
/**
* Finishes the removal of a detached view. This method will dispatch the detached from
* window event and notify the hierarchy change listener.
+ * <p>
+ * This method is intended to be lightweight and makes no assumptions about whether the
+ * parent or child should be redrawn. Proper use of this method will include also making
+ * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
+ * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
+ * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
+ * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
*
* @param child the child to be definitely removed from the view hierarchy
* @param animate if true and the view has an animation, the view is placed in the
@@ -3846,10 +3853,17 @@
/**
* Attaches a view to this view group. Attaching a view assigns this group as the parent,
- * sets the layout parameters and puts the view in the list of children so it can be retrieved
- * by calling {@link #getChildAt(int)}.
- *
- * This method should be called only for view which were detached from their parent.
+ * sets the layout parameters and puts the view in the list of children so that
+ * it can be retrieved by calling {@link #getChildAt(int)}.
+ * <p>
+ * This method is intended to be lightweight and makes no assumptions about whether the
+ * parent or child should be redrawn. Proper use of this method will include also making
+ * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
+ * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
+ * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
+ * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
+ * <p>
+ * This method should be called only for views which were detached from their parent.
*
* @param child the child to attach
* @param index the index at which the child should be attached
@@ -3881,10 +3895,13 @@
}
/**
- * Detaches a view from its parent. Detaching a view should be temporary and followed
- * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
- * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
- * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+ * Detaches a view from its parent. Detaching a view should be followed
+ * either by a call to
+ * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+ * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
+ * temporary; reattachment or removal should happen within the same drawing cycle as
+ * detachment. When a view is detached, its parent is null and cannot be retrieved by a
+ * call to {@link #getChildAt(int)}.
*
* @param child the child to detach
*
@@ -3899,10 +3916,13 @@
}
/**
- * Detaches a view from its parent. Detaching a view should be temporary and followed
- * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
- * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
- * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+ * Detaches a view from its parent. Detaching a view should be followed
+ * either by a call to
+ * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+ * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
+ * temporary; reattachment or removal should happen within the same drawing cycle as
+ * detachment. When a view is detached, its parent is null and cannot be retrieved by a
+ * call to {@link #getChildAt(int)}.
*
* @param index the index of the child to detach
*
@@ -3917,10 +3937,13 @@
}
/**
- * Detaches a range of view from their parent. Detaching a view should be temporary and followed
- * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
- * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
- * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+ * Detaches a range of views from their parents. Detaching a view should be followed
+ * either by a call to
+ * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+ * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
+ * temporary; reattachment or removal should happen within the same drawing cycle as
+ * detachment. When a view is detached, its parent is null and cannot be retrieved by a
+ * call to {@link #getChildAt(int)}.
*
* @param start the first index of the childrend range to detach
* @param count the number of children to detach
@@ -3936,10 +3959,13 @@
}
/**
- * Detaches all views from the parent. Detaching a view should be temporary and followed
- * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
- * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
- * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+ * Detaches all views from the parent. Detaching a view should be followed
+ * either by a call to
+ * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+ * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
+ * temporary; reattachment or removal should happen within the same drawing cycle as
+ * detachment. When a view is detached, its parent is null and cannot be retrieved by a
+ * call to {@link #getChildAt(int)}.
*
* @see #detachViewFromParent(View)
* @see #detachViewFromParent(int)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 725d9b5..a4c0235 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -214,6 +214,9 @@
boolean mTraversalScheduled;
int mTraversalBarrier;
boolean mWillDrawSoon;
+ /** Set to true while in performTraversals for detecting when die(true) is called from internal
+ * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */
+ boolean mIsInTraversal;
boolean mFitSystemWindowsRequested;
boolean mLayoutRequested;
boolean mFirst;
@@ -1104,6 +1107,7 @@
if (host == null || !mAdded)
return;
+ mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean newSurface = false;
@@ -1842,6 +1846,8 @@
mPendingTransitions.clear();
}
}
+
+ mIsInTraversal = false;
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
@@ -3956,7 +3962,9 @@
}
public void die(boolean immediate) {
- if (immediate) {
+ // Make sure we do execute immediately if we are in the middle of a traversal or the damage
+ // done by dispatchDetachedFromWindow will cause havoc on return.
+ if (immediate && !mIsInTraversal) {
doDie();
} else {
if (!mIsDrawing) {
@@ -4641,9 +4649,19 @@
// ViewAncestor never intercepts touch event, so this can be a no-op
}
- public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
- boolean immediate) {
- return scrollToRectOrFocus(rectangle, immediate);
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ final boolean scrolled = scrollToRectOrFocus(rectangle, immediate);
+ if (rectangle != null) {
+ mTempRect.set(rectangle);
+ mTempRect.offset(0, -mCurScrollY);
+ mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
+ try {
+ mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, immediate);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ return scrolled;
}
public void childHasTransientStateChanged(View child, boolean hasTransientState) {
diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl
new file mode 100644
index 0000000..23e927a
--- /dev/null
+++ b/core/java/android/view/WindowInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.
+*/
+
+package android.view;
+
+parcelable WindowInfo;
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
new file mode 100644
index 0000000..00875ae
--- /dev/null
+++ b/core/java/android/view/WindowInfo.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information the state of a window.
+ *
+ * @hide
+ */
+public class WindowInfo implements Parcelable {
+
+ private static final int MAX_POOL_SIZE = 20;
+
+ private static int UNDEFINED = -1;
+
+ private static Object sPoolLock = new Object();
+ private static WindowInfo sPool;
+ private static int sPoolSize;
+
+ private WindowInfo mNext;
+ private boolean mInPool;
+
+ public IBinder token;
+
+ public final Rect frame = new Rect();
+
+ public final Rect touchableRegion = new Rect();
+
+ public int type;
+
+ public float compatibilityScale;
+
+ public boolean visible;
+
+ public int displayId;
+
+ private WindowInfo() {
+ /* do nothing - reduce visibility */
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeStrongBinder(token);
+ parcel.writeParcelable(frame, 0);
+ parcel.writeParcelable(touchableRegion, 0);
+ parcel.writeInt(type);
+ parcel.writeFloat(compatibilityScale);
+ parcel.writeInt(visible ? 1 : 0);
+ parcel.writeInt(displayId);
+ recycle();
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ token = parcel.readStrongBinder();
+ frame.set((Rect) parcel.readParcelable(null));
+ touchableRegion.set((Rect) parcel.readParcelable(null));
+ type = parcel.readInt();
+ compatibilityScale = parcel.readFloat();
+ visible = (parcel.readInt() == 1);
+ displayId = parcel.readInt();
+ }
+
+ public static WindowInfo obtain(WindowInfo other) {
+ WindowInfo info = obtain();
+ info.token = other.token;
+ info.frame.set(other.frame);
+ info.touchableRegion.set(other.touchableRegion);
+ info.type = other.type;
+ info.displayId = other.displayId;
+ info.compatibilityScale = other.compatibilityScale;
+ info.visible = other.visible;
+ return info;
+ }
+
+ public static WindowInfo obtain() {
+ synchronized (sPoolLock) {
+ if (sPoolSize > 0) {
+ WindowInfo info = sPool;
+ sPool = info.mNext;
+ info.mNext = null;
+ info.mInPool = false;
+ sPoolSize--;
+ return info;
+ } else {
+ return new WindowInfo();
+ }
+ }
+ }
+
+ public void recycle() {
+ if (mInPool) {
+ throw new IllegalStateException("Already recycled.");
+ }
+ clear();
+ synchronized (sPoolLock) {
+ if (sPoolSize < MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ mInPool = true;
+ sPoolSize++;
+ }
+ }
+ }
+
+ private void clear() {
+ token = null;
+ frame.setEmpty();
+ touchableRegion.setEmpty();
+ type = UNDEFINED;
+ displayId = UNDEFINED;
+ visible = false;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final Parcelable.Creator<WindowInfo> CREATOR =
+ new Parcelable.Creator<WindowInfo>() {
+ public WindowInfo createFromParcel(Parcel parcel) {
+ WindowInfo info = WindowInfo.obtain();
+ info.initFromParcel(parcel);
+ return info;
+ }
+
+ public WindowInfo[] newArray(int size) {
+ return new WindowInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 123d9e7..fa2d4e8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,6 +16,8 @@
package android.view;
+import android.app.Presentation;
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.IBinder;
@@ -29,6 +31,17 @@
* The interface that apps use to talk to the window manager.
* <p>
* Use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> to get one of these.
+ * </p><p>
+ * Each window manager instance is bound to a particular {@link Display}.
+ * To obtain a {@link WindowManager} for a different display, use
+ * {@link Context#createDisplayContext} to obtain a {@link Context} for that
+ * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code>
+ * to get the WindowManager.
+ * </p><p>
+ * The simplest way to show a window on another display is to create a
+ * {@link Presentation}. The presentation will automatically obtain a
+ * {@link WindowManager} and {@link Context} for that display.
+ * </p>
*
* @see android.content.Context#getSystemService
* @see android.content.Context#WINDOW_SERVICE
@@ -49,12 +62,24 @@
}
/**
- * Use this method to get the default Display object.
- *
- * @return default Display object
+ * Returns the {@link Display} upon which this {@link WindowManager} instance
+ * will create new windows.
+ * <p>
+ * Despite the name of this method, the display that is returned is not
+ * necessarily the primary display of the system (see {@link Display#DEFAULT_DISPLAY}).
+ * The returned display could instead be a secondary display that this
+ * window manager instance is managing. Think of it as the display that
+ * this {@link WindowManager} instance uses by default.
+ * </p><p>
+ * To create windows on a different display, you need to obtain a
+ * {@link WindowManager} for that {@link Display}. (See the {@link WindowManager}
+ * class documentation for more information.)
+ * </p>
+ *
+ * @return The display that this window manager is managing.
*/
public Display getDefaultDisplay();
-
+
/**
* Special variation of {@link #removeView} that immediately invokes
* the given view hierarchy's {@link View#onDetachedFromWindow()
@@ -179,7 +204,9 @@
@ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS"),
@ViewDebug.IntToString(from = TYPE_HIDDEN_NAV_CONSUMER, to = "TYPE_HIDDEN_NAV_CONSUMER"),
@ViewDebug.IntToString(from = TYPE_DREAM, to = "TYPE_DREAM"),
- @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL")
+ @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"),
+ @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"),
+ @ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY, to = "TYPE_MAGNIFICATION_OVERLAY")
})
public int type;
@@ -435,6 +462,19 @@
public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;
/**
+ * Window type: Display overlay window. Used to simulate secondary display devices.
+ * @hide
+ */
+ public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
+
+ /**
+ * Window type: Magnification overlay window. Used to highlight the magnified
+ * portion of a display when accessibility magnification is enabled.
+ * @hide
+ */
+ public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index bf061df..52d79f8 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,9 +16,6 @@
package android.view;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-
/**
* Provides low-level communication with the system window manager for
* operations that are bound to a particular context, display or parent window.
@@ -47,24 +44,24 @@
*/
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- private final Context mContext;
private final Display mDisplay;
private final Window mParentWindow;
- public WindowManagerImpl(Context context, int displayId) {
- mContext = context;
- mDisplay = DisplayManager.getInstance().getDisplay(displayId, mContext);
- mParentWindow = null;
+ public WindowManagerImpl(Display display) {
+ this(display, null);
}
- private WindowManagerImpl(Context context, Display display, Window parentWindow) {
- mContext = context;
+ private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
- return new WindowManagerImpl(mContext, mDisplay, parentWindow);
+ return new WindowManagerImpl(mDisplay, parentWindow);
+ }
+
+ public WindowManagerImpl createPresentationWindowManager(Display display) {
+ return new WindowManagerImpl(display, mParentWindow);
}
@Override
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7173d1d..75554da 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -123,7 +123,7 @@
/**
* This key event should put the device to sleep (and engage keyguard if necessary)
* To be returned from {@link #interceptKeyBeforeQueueing}.
- * Do not return this and {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
+ * Do not return this and {@link #ACTION_WAKE_UP} or {@link #ACTION_PASS_TO_USER}.
*/
public final static int ACTION_GO_TO_SLEEP = 0x00000004;
@@ -338,6 +338,12 @@
* Check whether the process hosting this window is currently alive.
*/
public boolean isAlive();
+
+ /**
+ * Check if window is on {@link Display#DEFAULT_DISPLAY}.
+ * @return true if window is on default display.
+ */
+ public boolean isDefaultDisplay();
}
/**
@@ -707,7 +713,7 @@
* @param isScreenOn True if the screen is already on
*
* @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
- * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
+ * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags.
*/
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
@@ -721,7 +727,7 @@
* @param policyFlags The policy flags associated with the motion.
*
* @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
- * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
+ * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags.
*/
public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags);
@@ -762,12 +768,14 @@
/**
* Called when layout of the windows is about to start.
*
+ * @param isDefaultDisplay true if window is on {@link Display#DEFAULT_DISPLAY}.
* @param displayWidth The current full width of the screen.
* @param displayHeight The current full height of the screen.
* @param displayRotation The current rotation being applied to the base
* window.
*/
- public void beginLayoutLw(int displayWidth, int displayHeight, int displayRotation);
+ public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
+ int displayRotation);
/**
* Return the rectangle of the screen currently covered by system decorations.
@@ -827,32 +835,33 @@
static final int FINISH_LAYOUT_REDO_ANIM = 0x0008;
/**
- * Called when animation of the windows is about to start.
+ * Called following layout of all windows before each window has policy applied.
*
* @param displayWidth The current full width of the screen.
* @param displayHeight The current full height of the screen.
*/
- public void beginAnimationLw(int displayWidth, int displayHeight);
+ public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight);
/**
- * Called each time a window is animating.
+ * Called following layout of all window to apply policy to each window.
*
* @param win The window being positioned.
* @param attrs The LayoutParams of the window.
*/
- public void animatingWindowLw(WindowState win,
+ public void applyPostLayoutPolicyLw(WindowState win,
WindowManager.LayoutParams attrs);
/**
- * Called when animation of the windows is finished. If in this function you do
- * something that may have modified the animation state of another window,
- * be sure to return true in order to perform another animation frame.
+ * Called following layout of all windows and after policy has been applied
+ * to each window. If in this function you do
+ * something that may have modified the animation state of another window,
+ * be sure to return non-zero in order to perform another pass through layout.
*
* @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
* {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link #FINISH_LAYOUT_REDO_ANIM}.
*/
- public int finishAnimationLw();
+ public int finishPostLayoutPolicyLw();
/**
* Return true if it is okay to perform animations for an app transition
@@ -1065,9 +1074,9 @@
* Inform the policy that the user has chosen a preferred orientation ("rotation lock").
*
* @param mode One of {@link WindowManagerPolicy#USER_ROTATION_LOCKED} or
- * {@link * WindowManagerPolicy#USER_ROTATION_FREE}.
+ * {@link WindowManagerPolicy#USER_ROTATION_FREE}.
* @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
- * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
+ * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
*/
public void setUserRotationMode(int mode, int rotation);
@@ -1097,6 +1106,14 @@
public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
/**
+ * Returns whether magnification can be applied to the given window type.
+ *
+ * @param attrs The window's LayoutParams.
+ * @return Whether magnification can be applied.
+ */
+ public boolean canMagnifyWindow(WindowManager.LayoutParams attrs);
+
+ /**
* Print the WindowManagerPolicy's state into the given stream.
*
* @param prefix Text to print at the front of each line.
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 131f0ae..08e30aa 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Printer;
+import android.util.Slog;
import android.util.Xml;
import java.io.IOException;
@@ -169,7 +170,10 @@
a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAuxiliary, false),
a.getBoolean(com.android.internal.R.styleable
- .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false));
+ .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false),
+ a.getInt(com.android.internal.R.styleable
+ .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */)
+ );
if (!subtype.isAuxiliary()) {
mIsAuxIme = false;
}
@@ -194,6 +198,9 @@
final InputMethodSubtype subtype = additionalSubtypes.get(i);
if (!mSubtypes.contains(subtype)) {
mSubtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index b7c94a3..7895e6f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -55,13 +55,14 @@
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
private final int mSubtypeNameResId;
+ private final int mSubtypeId;
private final String mSubtypeLocale;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
private volatile HashMap<String, String> mExtraValueHashMapCache;
/**
- * Constructor.
+ * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -87,7 +88,7 @@
}
/**
- * Constructor.
+ * Constructor with no subtype ID specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -112,6 +113,41 @@
*/
public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
+ this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
+ overridesImplicitlyEnabledSubtype, 0);
+ }
+
+ /**
+ * Constructor.
+ * @param nameId Resource ID of the subtype name string. The string resource may have exactly
+ * one %s in it. If there is, the %s part will be replaced with the locale's display name by
+ * the formatter. Please refer to {@link #getDisplayName} for details.
+ * @param iconId Resource ID of the subtype icon drawable.
+ * @param locale The locale supported by the subtype
+ * @param mode The mode supported by the subtype
+ * @param extraValue The extra value of the subtype. This string is free-form, but the API
+ * supplies tools to deal with a key-value comma-separated list; see
+ * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
+ * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
+ * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
+ * the Settings even when this subtype is enabled. Please note that this subtype will still
+ * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
+ * to this subtype while an IME is shown. The framework will never switch the current IME to
+ * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
+ * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
+ * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
+ * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
+ * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
+ * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
+ * Having an "automatic" subtype is an example use of this flag.
+ * @param id The unique ID for the subtype. The input method framework keeps track of enabled
+ * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
+ * other attributes are different. If the ID is unspecified or 0,
+ * Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+ */
+ public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+ boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
mSubtypeNameResId = nameId;
mSubtypeIconResId = iconId;
mSubtypeLocale = locale != null ? locale : "";
@@ -119,8 +155,11 @@
mSubtypeExtraValue = extraValue != null ? extraValue : "";
mIsAuxiliary = isAuxiliary;
mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
+ // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
+ mSubtypeHashCode = id != 0 ? id : hashCodeInternal(mSubtypeLocale, mSubtypeMode,
+ mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeId = id;
}
InputMethodSubtype(Parcel source) {
@@ -135,8 +174,8 @@
mSubtypeExtraValue = s != null ? s : "";
mIsAuxiliary = (source.readInt() == 1);
mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeHashCode = source.readInt();
+ mSubtypeId = source.readInt();
}
/**
@@ -288,6 +327,9 @@
public boolean equals(Object o) {
if (o instanceof InputMethodSubtype) {
InputMethodSubtype subtype = (InputMethodSubtype) o;
+ if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
+ return (subtype.hashCode() == hashCode());
+ }
return (subtype.hashCode() == hashCode())
&& (subtype.getNameResId() == getNameResId())
&& (subtype.getMode().equals(getMode()))
@@ -313,6 +355,8 @@
dest.writeString(mSubtypeExtraValue);
dest.writeInt(mIsAuxiliary ? 1 : 0);
dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
+ dest.writeInt(mSubtypeHashCode);
+ dest.writeInt(mSubtypeId);
}
public static final Parcelable.Creator<InputMethodSubtype> CREATOR
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 7dfb5bb..fd73fda 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -279,6 +279,7 @@
}
if (!shouldInjectJavaScript(url)) {
+ mAccessibilityScriptInjected = false;
toggleFallbackAccessibilityInjector(true);
return;
}
@@ -292,6 +293,23 @@
}
/**
+ * Adjusts the accessibility injection state to reflect changes in the
+ * JavaScript enabled state.
+ *
+ * @param enabled Whether JavaScript is enabled.
+ */
+ public void updateJavaScriptEnabled(boolean enabled) {
+ if (enabled) {
+ addAccessibilityApisIfNecessary();
+ } else {
+ removeAccessibilityApisIfNecessary();
+ }
+
+ // We have to reload the page after adding or removing APIs.
+ mWebView.reload();
+ }
+
+ /**
* Toggles the non-JavaScript method for handling accessibility.
*
* @param enabled {@code true} to enable the non-JavaScript method, or
diff --git a/core/java/android/webkit/BrowserDownloadListener.java b/core/java/android/webkit/BrowserDownloadListener.java
new file mode 100644
index 0000000..724cc62
--- /dev/null
+++ b/core/java/android/webkit/BrowserDownloadListener.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.webkit;
+
+/**
+ * An abstract download listener that allows passing extra information as
+ * part of onDownloadStart callback.
+ * @hide
+ */
+public abstract class BrowserDownloadListener implements DownloadListener {
+
+ /**
+ * Notify the host application that a file should be downloaded
+ * @param url The full url to the content that should be downloaded
+ * @param userAgent the user agent to be used for the download.
+ * @param contentDisposition Content-disposition http header, if
+ * present.
+ * @param mimetype The mimetype of the content reported by the server
+ * @param referer The referer associated with this url
+ * @param contentLength The file size reported by the server
+ */
+ public abstract void onDownloadStart(String url, String userAgent,
+ String contentDisposition, String mimetype, String referer,
+ long contentLength);
+
+
+ /**
+ * Notify the host application that a file should be downloaded
+ * @param url The full url to the content that should be downloaded
+ * @param userAgent the user agent to be used for the download.
+ * @param contentDisposition Content-disposition http header, if
+ * present.
+ * @param mimetype The mimetype of the content reported by the server
+ * @param contentLength The file size reported by the server
+ */
+ @Override
+ public void onDownloadStart(String url, String userAgent,
+ String contentDisposition, String mimetype, long contentLength) {
+
+ onDownloadStart(url, userAgent, contentDisposition, mimetype, null,
+ contentLength);
+ }
+}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index fe812af..6074a0c 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -247,7 +247,6 @@
mWebViewCore = w;
mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
- mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
AssetManager am = context.getAssets();
nativeCreateFrame(w, am, proxy.getBackForwardList());
@@ -598,8 +597,6 @@
}
}
mRemovedJavaScriptObjects.clear();
-
- stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
}
/**
@@ -1137,7 +1134,7 @@
* DownloadListener.
*/
private void downloadStart(String url, String userAgent,
- String contentDisposition, String mimeType, long contentLength) {
+ String contentDisposition, String mimeType, String referer, long contentLength) {
// This will only work if the url ends with the filename
if (mimeType.isEmpty()) {
try {
@@ -1157,7 +1154,7 @@
mKeyStoreHandler = new KeyStoreHandler(mimeType);
} else {
mCallbackProxy.onDownloadStart(url, userAgent,
- contentDisposition, mimeType, contentLength);
+ contentDisposition, mimeType, referer, contentLength);
}
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 6b87ded..b47cba8 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -452,10 +452,16 @@
String contentDisposition =
msg.getData().getString("contentDisposition");
String mimetype = msg.getData().getString("mimetype");
+ String referer = msg.getData().getString("referer");
Long contentLength = msg.getData().getLong("contentLength");
- mDownloadListener.onDownloadStart(url, userAgent,
- contentDisposition, mimetype, contentLength);
+ if (mDownloadListener instanceof BrowserDownloadListener) {
+ ((BrowserDownloadListener) mDownloadListener).onDownloadStart(url,
+ userAgent, contentDisposition, mimetype, referer, contentLength);
+ } else {
+ mDownloadListener.onDownloadStart(url, userAgent,
+ contentDisposition, mimetype, contentLength);
+ }
}
break;
@@ -1179,7 +1185,8 @@
* return false.
*/
public boolean onDownloadStart(String url, String userAgent,
- String contentDisposition, String mimetype, long contentLength) {
+ String contentDisposition, String mimetype, String referer,
+ long contentLength) {
// Do an unsynchronized quick check to avoid posting if no callback has
// been set.
if (mDownloadListener == null) {
@@ -1192,6 +1199,7 @@
bundle.putString("url", url);
bundle.putString("userAgent", userAgent);
bundle.putString("mimetype", mimetype);
+ bundle.putString("referer", referer);
bundle.putLong("contentLength", contentLength);
bundle.putString("contentDisposition", contentDisposition);
sendMessage(msg);
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index d1f8b4b..1bbe7bb 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -1122,6 +1122,7 @@
if (mJavaScriptEnabled != flag) {
mJavaScriptEnabled = flag;
postSync();
+ mWebView.updateJavaScriptEnabled(flag);
}
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 9df4852..9334036 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -687,6 +687,10 @@
// It's used to dismiss the dialog in destroy if not done before.
private AlertDialog mListBoxDialog = null;
+ // Reference to the save password dialog so it can be dimissed in
+ // destroy if not done before.
+ private AlertDialog mSavePasswordDialog = null;
+
static final String LOGTAG = "webview";
private ZoomManager mZoomManager;
@@ -1342,20 +1346,40 @@
private void onHandleUiTouchEvent(MotionEvent ev) {
final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
+ mZoomManager.getScaleGestureDetector();
- float x = ev.getX();
- float y = ev.getY();
+ int action = ev.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (configChanged) {
+ mLastTouchX = Math.round(x);
+ mLastTouchY = Math.round(y);
+ mLastTouchTime = ev.getEventTime();
+ mWebView.cancelLongPress();
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ }
if (detector != null) {
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
mLastTouchTime = ev.getEventTime();
- x = detector.getFocusX();
- y = detector.getFocusY();
- mWebView.cancelLongPress();
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
if (!mZoomManager.supportsPanDuringZoom()) {
return;
}
@@ -1366,14 +1390,9 @@
}
}
- int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
cancelTouch();
action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
- // set mLastTouchX/Y to the remaining points for multi-touch.
- mLastTouchX = Math.round(x);
- mLastTouchY = Math.round(y);
} else if (action == MotionEvent.ACTION_MOVE) {
// negative x or y indicate it is on the edge, skip it.
if (x < 0 || y < 0) {
@@ -1633,6 +1652,12 @@
mZoomManager.updateMultiTouchSupport(context);
}
+ void updateJavaScriptEnabled(boolean enabled) {
+ if (isAccessibilityInjectionEnabled()) {
+ getAccessibilityInjector().updateJavaScriptEnabled(enabled);
+ }
+ }
+
private void init() {
OnTrimMemoryListener.init(mContext);
mWebView.setWillNotDraw(false);
@@ -1830,7 +1855,7 @@
neverRemember.getData().putString("password", password);
neverRemember.obj = resumeMsg;
- new AlertDialog.Builder(mContext)
+ mSavePasswordDialog = new AlertDialog.Builder(mContext)
.setTitle(com.android.internal.R.string.save_password_label)
.setMessage(com.android.internal.R.string.save_password_message)
.setPositiveButton(com.android.internal.R.string.save_password_notnow,
@@ -1841,6 +1866,7 @@
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNeutralButton(com.android.internal.R.string.save_password_remember,
@@ -1851,6 +1877,7 @@
remember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNegativeButton(com.android.internal.R.string.save_password_never,
@@ -1861,6 +1888,7 @@
neverRemember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setOnCancelListener(new OnCancelListener() {
@@ -1870,6 +1898,7 @@
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
}).show();
// Return true so that WebViewCore will pause while the dialog is
@@ -2109,6 +2138,10 @@
mListBoxDialog.dismiss();
mListBoxDialog = null;
}
+ if (mSavePasswordDialog != null) {
+ mSavePasswordDialog.dismiss();
+ mSavePasswordDialog = null;
+ }
if (mWebViewCore != null) {
// Tell WebViewCore to destroy itself
synchronized (this) {
@@ -4367,7 +4400,7 @@
// A multi-finger gesture can look like a long press; make sure we don't take
// long press actions if we're scaling.
- final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
if (detector != null && detector.isInProgress()) {
return false;
}
@@ -5805,7 +5838,7 @@
* and the middle point for multi-touch.
*/
private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
long eventTime = event.getEventTime();
@@ -6981,6 +7014,8 @@
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ // Check if we are destroyed
+ if (mWebViewCore == null) return false;
// FIXME: If a subwindow is showing find, and the user touches the
// background window, it can steal focus.
if (mFindIsUp) return false;
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 8830119..80a6782 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -204,7 +204,7 @@
*/
private boolean mAllowPanAndScale;
- // use the framework's ScaleGestureDetector to handle multi-touch
+ // use the framework's ScaleGestureDetector to handle scaling gestures
private ScaleGestureDetector mScaleDetector;
private boolean mPinchToZoomAnimating = false;
@@ -768,7 +768,7 @@
return isZoomAnimating();
}
- public ScaleGestureDetector getMultiTouchGestureDetector() {
+ public ScaleGestureDetector getScaleGestureDetector() {
return mScaleDetector;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 423135f..920d44f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2158,7 +2158,9 @@
if (mAccessibilityDelegate == null) {
mAccessibilityDelegate = new ListItemAccessibilityDelegate();
}
- child.setAccessibilityDelegate(mAccessibilityDelegate);
+ if (child.getAccessibilityDelegate() == null) {
+ child.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
}
return child;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index e217e4f..34f78c6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -107,6 +107,9 @@
}
if (thumb != null) {
thumb.setCallback(this);
+ if (canResolveLayoutDirection()) {
+ thumb.setLayoutDirection(getResolvedLayoutDirection());
+ }
// Assuming the thumb drawable is symmetric, set the thumb offset
// such that the thumb will hang halfway off either edge of the
@@ -303,7 +306,16 @@
// Canvas will be translated, so 0,0 is where we start drawing
thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
}
-
+
+ @Override
+ public void onResolveDrawables(int layoutDirection) {
+ super.onResolveDrawables(layoutDirection);
+
+ if (mThumb != null) {
+ mThumb.setLayoutDirection(layoutDirection);
+ }
+ }
+
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -409,15 +421,25 @@
int x = (int)event.getX();
float scale;
float progress = 0;
- if (x < mPaddingLeft) {
- scale = 0.0f;
- } else if (x > width - mPaddingRight) {
- scale = 1.0f;
+ if (isLayoutRtl()) {
+ if (x > width - mPaddingRight) {
+ scale = 0.0f;
+ } else if (x < mPaddingLeft) {
+ scale = 1.0f;
+ } else {
+ scale = (float)(available - x + mPaddingLeft) / (float)available;
+ progress = mTouchProgressOffset;
+ }
} else {
- scale = (float)(x - mPaddingLeft) / (float)available;
- progress = mTouchProgressOffset;
+ if (x < mPaddingLeft) {
+ scale = 0.0f;
+ } else if (x > width - mPaddingRight) {
+ scale = 1.0f;
+ } else {
+ scale = (float)(x - mPaddingLeft) / (float)available;
+ progress = mTouchProgressOffset;
+ }
}
-
final int max = getMax();
progress += scale * max;
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 41e8452..e4d4981 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.app.SearchManager.OnDismissListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
@@ -579,6 +580,23 @@
}
/**
+ * Set a listener that will be invoked whenever the AutoCompleteTextView's
+ * list of completions is dismissed.
+ * @param dismissListener Listener to invoke when completions are dismissed
+ */
+ public void setOnDismissListener(final OnDismissListener dismissListener) {
+ PopupWindow.OnDismissListener wrappedListener = null;
+ if (dismissListener != null) {
+ wrappedListener = new PopupWindow.OnDismissListener() {
+ @Override public void onDismiss() {
+ dismissListener.onDismiss();
+ }
+ };
+ }
+ mPopup.setOnDismissListener(wrappedListener);
+ }
+
+ /**
* <p>Returns a filterable list adapter used for auto completion.</p>
*
* @return a data adapter used for auto completion
@@ -1207,6 +1225,19 @@
}
/**
+ * Listener to respond to the AutoCompleteTextView's completion list being dismissed.
+ * @see AutoCompleteTextView#setOnDismissListener(OnDismissListener)
+ */
+ public interface OnDismissListener {
+ /**
+ * This method will be invoked whenever the AutoCompleteTextView's list
+ * of completion options has been dismissed and is no longer available
+ * for user interaction.
+ */
+ void onDismiss();
+ }
+
+ /**
* Allows us a private hook into the on click event without preventing users from setting
* their own click listener.
*/
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index e011c13..938979a 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -29,6 +29,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.util.SparseBooleanArray;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -1490,6 +1491,10 @@
View focusLayoutRestoreView = null;
+ AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
+ View accessibilityFocusLayoutRestoreView = null;
+ int accessibilityFocusPosition = INVALID_POSITION;
+
// Remember stuff we will need down below
switch (mLayoutMode) {
case LAYOUT_SET_SELECTION:
@@ -1584,6 +1589,25 @@
requestFocus();
}
+ // Remember which child, if any, had accessibility focus.
+ final View accessFocusedView = getViewRootImpl().getAccessibilityFocusedHost();
+ if (accessFocusedView != null) {
+ final View accessFocusedChild = findAccessibilityFocusedChild(accessFocusedView);
+ if (accessFocusedChild != null) {
+ if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
+ // If the views won't be changing, try to maintain focus
+ // on the current view host and (if applicable) its
+ // virtual view.
+ accessibilityFocusLayoutRestoreView = accessFocusedView;
+ accessibilityFocusLayoutRestoreNode = getViewRootImpl()
+ .getAccessibilityFocusedVirtualView();
+ } else {
+ // Otherwise, try to maintain focus at the same position.
+ accessibilityFocusPosition = getPositionForView(accessFocusedChild);
+ }
+ }
+ }
+
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
@@ -1682,6 +1706,22 @@
}
}
+ // Attempt to restore accessibility focus.
+ if (accessibilityFocusLayoutRestoreNode != null) {
+ accessibilityFocusLayoutRestoreNode.performAction(
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ } else if (accessibilityFocusLayoutRestoreView != null) {
+ accessibilityFocusLayoutRestoreView.requestAccessibilityFocus();
+ } else if (accessibilityFocusPosition != INVALID_POSITION) {
+ // Bound the position within the visible children.
+ final int position = MathUtils.constrain(
+ (accessibilityFocusPosition - mFirstPosition), 0, (getChildCount() - 1));
+ final View restoreView = getChildAt(position);
+ if (restoreView != null) {
+ restoreView.requestAccessibilityFocus();
+ }
+ }
+
// tell focus view we are done mucking with it, if it is still in
// our view hierarchy.
if (focusLayoutRestoreView != null
@@ -1713,6 +1753,22 @@
}
/**
+ * @param focusedView the view that has accessibility focus.
+ * @return the direct child that contains accessibility focus.
+ */
+ private View findAccessibilityFocusedChild(View focusedView) {
+ ViewParent viewParent = focusedView.getParent();
+ while ((viewParent instanceof View) && (viewParent != this)) {
+ focusedView = (View) viewParent;
+ viewParent = viewParent.getParent();
+ }
+ if (!(viewParent instanceof View)) {
+ return null;
+ }
+ return focusedView;
+ }
+
+ /**
* @param child a direct child of this list.
* @return Whether child is a header or footer view.
*/
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index fc35f05..f76ab2b 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -477,7 +477,8 @@
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
+ || keyCode == KeyEvent.KEYCODE_CAMERA) {
// don't show the controls for volume adjustment
return super.dispatchKeyEvent(event);
} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index eabcb80..dbc777e 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -2305,22 +2305,26 @@
}
private void sendAccessibilityEventForVirtualText(int eventType) {
- AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- mInputText.onInitializeAccessibilityEvent(event);
- mInputText.onPopulateAccessibilityEvent(event);
- event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
- requestSendAccessibilityEvent(NumberPicker.this, event);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ mInputText.onInitializeAccessibilityEvent(event);
+ mInputText.onPopulateAccessibilityEvent(event);
+ event.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
}
private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
String text) {
- AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- event.setClassName(Button.class.getName());
- event.setPackageName(mContext.getPackageName());
- event.getText().add(text);
- event.setEnabled(NumberPicker.this.isEnabled());
- event.setSource(NumberPicker.this, virtualViewId);
- requestSendAccessibilityEvent(NumberPicker.this, event);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setClassName(Button.class.getName());
+ event.setPackageName(mContext.getPackageName());
+ event.getText().add(text);
+ event.setEnabled(NumberPicker.this.isEnabled());
+ event.setSource(NumberPicker.this, virtualViewId);
+ requestSendAccessibilityEvent(NumberPicker.this, event);
+ }
}
private void findAccessibilityNodeInfosByTextInChild(String searchedLowerCase,
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 860e583..b6d0995 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -478,8 +478,8 @@
d.setCallback(this);
}
mIndeterminateDrawable = d;
- if (mIndeterminateDrawable != null) {
- mIndeterminateDrawable.setLayoutDirection(getLayoutDirection());
+ if (mIndeterminateDrawable != null && canResolveLayoutDirection()) {
+ mIndeterminateDrawable.setLayoutDirection(getResolvedLayoutDirection());
}
if (mIndeterminate) {
mCurrentDrawable = d;
@@ -520,7 +520,9 @@
if (d != null) {
d.setCallback(this);
- d.setLayoutDirection(getLayoutDirection());
+ if (canResolveLayoutDirection()) {
+ d.setLayoutDirection(getResolvedLayoutDirection());
+ }
// Make sure the ProgressBar is always tall enough
int drawableHeight = d.getMinimumHeight();
@@ -564,6 +566,20 @@
}
@Override
+ public void onResolveDrawables(int layoutDirection) {
+ final Drawable d = mCurrentDrawable;
+ if (d != null) {
+ d.setLayoutDirection(layoutDirection);
+ }
+ if (mIndeterminateDrawable != null) {
+ mIndeterminateDrawable.setLayoutDirection(layoutDirection);
+ }
+ if (mProgressDrawable != null) {
+ mProgressDrawable.setLayoutDirection(layoutDirection);
+ }
+ }
+
+ @Override
public void postInvalidate() {
if (!mNoInvalidate) {
super.postInvalidate();
@@ -652,6 +668,9 @@
if (d instanceof LayerDrawable) {
progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
+ if (progressDrawable != null && canResolveLayoutDirection()) {
+ progressDrawable.setLayoutDirection(getResolvedLayoutDirection());
+ }
}
final int level = (int) (scale * MAX_LEVEL);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index a30567f..9b62a51 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1517,6 +1517,9 @@
// because the voice search activity will always need to insert "QUERY" into
// it anyway.
Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
+ }
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary extras
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f502de4..dd2ff35 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -302,6 +302,7 @@
// The alignment to pass to Layout, or null if not resolved.
private Layout.Alignment mLayoutAlignment;
+ private int mResolvedTextAlignment;
private boolean mResolvedDrawables;
@@ -4462,9 +4463,6 @@
mTemporaryDetach = false;
- // Resolve drawables as the layout direction has been resolved
- resolveDrawables();
-
if (mEditor != null) mEditor.onAttachedToWindow();
}
@@ -5639,9 +5637,8 @@
@Override
public void onResolvedLayoutDirectionReset() {
if (mLayoutAlignment != null) {
- int resolvedTextAlignment = getResolvedTextAlignment();
- if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
- resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+ if (mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+ mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
mLayoutAlignment = null;
}
}
@@ -5649,8 +5646,8 @@
private Layout.Alignment getLayoutAlignment() {
if (mLayoutAlignment == null) {
- int textAlign = getResolvedTextAlignment();
- switch (textAlign) {
+ mResolvedTextAlignment = getResolvedTextAlignment();
+ switch (mResolvedTextAlignment) {
case TEXT_ALIGNMENT_GRAVITY:
switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
case Gravity.START:
@@ -8355,7 +8352,7 @@
*/
@Override
public CharSequence getIterableTextForAccessibility() {
- if (getContentDescription() == null) {
+ if (!TextUtils.isEmpty(mText)) {
if (!(mText instanceof Spannable)) {
setText(mText, BufferType.SPANNABLE);
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 053ade7..485bd37 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -305,12 +305,14 @@
private static class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
+ @Override
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
+ @Override
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked by handleShow()
@@ -329,7 +331,7 @@
View mView;
View mNextView;
-
+
WindowManager mWM;
TN() {
@@ -350,6 +352,7 @@
/**
* schedule handleShow into the right thread
*/
+ @Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
@@ -358,6 +361,7 @@
/**
* schedule handleHide into the right thread
*/
+ @Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
@@ -370,11 +374,12 @@
// remove the old view if necessary
handleHide();
mView = mNextView;
- mWM = (WindowManager)mView.getContext().getSystemService(Context.WINDOW_SERVICE);
+ mWM = (WindowManager)mView.getContext().getApplicationContext()
+ .getSystemService(Context.WINDOW_SERVICE);
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity, config.layoutDirection);
+ final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 6a68240..eee914e 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -329,8 +329,21 @@
}
/**
+ * Returns whether the current View should be animated the first time the ViewAnimator
+ * is displayed.
+ *
+ * @return true if the current View will be animated the first time it is displayed,
+ * false otherwise.
+ *
+ * @see #setAnimateFirstView(boolean)
+ */
+ public boolean getAnimateFirstView() {
+ return mAnimateFirstTime;
+ }
+
+ /**
* Indicates whether the current View should be animated the first time
- * the ViewAnimation is displayed.
+ * the ViewAnimator is displayed.
*
* @param animate True to animate the current View the first time it is displayed,
* false otherwise.
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 7d1231e..f173327 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -243,10 +243,9 @@
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
- config.locale = locale;
-
- // indicate this isn't some passing default - the user wants this remembered
- config.userSetLocale = true;
+ // Will set userSetLocale to indicate this isn't some passing default - the user
+ // wants this remembered
+ config.setLocale(locale);
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 650681a..3477a90 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -49,6 +49,7 @@
sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
sPackageFilt.addDataScheme("package");
sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+ sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
}
@@ -136,6 +137,9 @@
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
return false;
}
+
+ public void onHandleUserStop(Intent intent, int userHandle) {
+ }
public void onUidRemoved(int uid) {
}
@@ -307,6 +311,10 @@
intent.getIntExtra(Intent.EXTRA_UID, 0), true);
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
+ onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
mAppearingPackages = pkgList;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index d6f1807..d24513a 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -529,6 +529,8 @@
niceName = arg.substring(arg.indexOf('=') + 1);
} else if (arg.equals("--mount-external-multiuser")) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+ } else if (arg.equals("--mount-external-multiuser-all")) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
} else {
break;
}
diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java
index 350e006..4ecbd16 100644
--- a/core/java/com/android/internal/widget/LockSettingsService.java
+++ b/core/java/com/android/internal/widget/LockSettingsService.java
@@ -23,6 +23,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Binder;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -173,7 +174,8 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PATTERN_FILE;
} else {
- return dataSystemDirectory + "users/" + userId + "/" + LOCK_PATTERN_FILE;
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
+ .getAbsolutePath();
}
}
@@ -185,7 +187,8 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PASSWORD_FILE;
} else {
- return dataSystemDirectory + "users/" + userId + "/" + LOCK_PASSWORD_FILE;
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
+ .getAbsolutePath();
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f950d3d..9d45479 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -46,6 +46,7 @@
android_emoji_EmojiFactory.cpp \
android_view_DisplayEventReceiver.cpp \
android_view_Surface.cpp \
+ android_view_SurfaceSession.cpp \
android_view_TextureView.cpp \
android_view_InputChannel.cpp \
android_view_InputDevice.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0c88297..55563a8 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -121,6 +121,7 @@
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_HardwareRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
+extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteConnection(JNIEnv* env);
@@ -1094,6 +1095,7 @@
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_HardwareRenderer),
REG_JNI(register_android_view_Surface),
+ REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_TextureView),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 3c27caf..1bba5b4 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -298,8 +298,18 @@
}
bool success = false;
- SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
- if (NULL != strm) {
+ if (NULL != bitmap) {
+ SkAutoLockPixels alp(*bitmap);
+
+ if (NULL == bitmap->getPixels()) {
+ return false;
+ }
+
+ SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+ if (NULL == strm) {
+ return false;
+ }
+
SkImageEncoder* encoder = SkImageEncoder::Create(fm);
if (NULL != encoder) {
success = encoder->encodeStream(strm, *bitmap, quality);
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 074afa3..21162f4 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -436,7 +436,7 @@
void setSurface(jobject _surface) {
if (_surface != NULL) {
- nativeWindow = android_Surface_getNativeWindow(env, _surface);
+ nativeWindow = android_view_Surface_getNativeWindow(env, _surface);
} else {
nativeWindow = NULL;
}
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index c271aeb..b1664c6 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -549,7 +549,7 @@
goto exit;
}
- window = android::android_Surface_getNativeWindow(_env, win);
+ window = android::android_view_Surface_getNativeWindow(_env, win);
if (window == NULL)
goto not_valid_surface;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 04dc49f..881d9a0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -729,11 +729,6 @@
return IPCThreadState::self()->getCallingUid();
}
-static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->getOrigCallingUid();
-}
-
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -805,7 +800,6 @@
/* name, signature, funcPtr */
{ "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
- { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9fc73a4..3538fef 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -46,6 +46,7 @@
#include <OpenGLRenderer.h>
#include <SkiaShader.h>
#include <SkiaColorFilter.h>
+#include <Stencil.h>
#include <Rect.h>
#include <TextLayout.h>
@@ -150,7 +151,7 @@
}
static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
- return OpenGLRenderer::getStencilSize();
+ return Stencil::getStencilSize();
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index bada329..4fbfab6 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -18,15 +18,19 @@
#include <stdio.h>
+#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include "android/graphics/GraphicsJNI.h"
+#include "android/graphics/Region.h"
#include <binder/IMemory.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceTexture.h>
+#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -41,445 +45,50 @@
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_view_SurfaceSession.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <utils/misc.h>
+#include <ScopedUtfChars.h>
+
// ----------------------------------------------------------------------------
namespace android {
-enum {
- // should match Parcelable.java
- PARCELABLE_WRITE_RETURN_VALUE = 0x0001
-};
-
-// ----------------------------------------------------------------------------
-
static const char* const OutOfResourcesException =
"android/view/Surface$OutOfResourcesException";
-const char* const kSurfaceSessionClassPathName = "android/view/SurfaceSession";
-const char* const kSurfaceClassPathName = "android/view/Surface";
+static struct {
+ jclass clazz;
+ jfieldID mNativeSurface;
+ jfieldID mNativeSurfaceControl;
+ jfieldID mGenerationId;
+ jfieldID mCanvas;
+ jfieldID mCanvasSaveCount;
+} gSurfaceClassInfo;
-struct sso_t {
- jfieldID client;
-};
-static sso_t sso;
+static struct {
+ jfieldID left;
+ jfieldID top;
+ jfieldID right;
+ jfieldID bottom;
+} gRectClassInfo;
-struct so_t {
- jfieldID surfaceControl;
- jfieldID surfaceGenerationId;
- jfieldID surface;
- jfieldID saveCount;
- jfieldID canvas;
-};
-static so_t so;
+static struct {
+ jfieldID mNativeCanvas;
+ jfieldID mSurfaceFormat;
+} gCanvasClassInfo;
-struct ro_t {
- jfieldID l;
- jfieldID t;
- jfieldID r;
- jfieldID b;
-};
-static ro_t ro;
+static struct {
+ jfieldID width;
+ jfieldID height;
+ jfieldID refreshRate;
+ jfieldID density;
+ jfieldID xDpi;
+ jfieldID yDpi;
+} gPhysicalDisplayInfoClassInfo;
-struct po_t {
- jfieldID x;
- jfieldID y;
-};
-static po_t po;
-
-struct co_t {
- jfieldID surfaceFormat;
-};
-static co_t co;
-
-struct no_t {
- jfieldID native_canvas;
- jfieldID native_region;
- jfieldID native_parcel;
-};
-static no_t no;
-
-
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-
-static void SurfaceSession_init(JNIEnv* env, jobject clazz)
-{
- sp<SurfaceComposerClient> client = new SurfaceComposerClient;
- client->incStrong(clazz);
- env->SetIntField(clazz, sso.client, (int)client.get());
-}
-
-static void SurfaceSession_destroy(JNIEnv* env, jobject clazz)
-{
- SurfaceComposerClient* client =
- (SurfaceComposerClient*)env->GetIntField(clazz, sso.client);
- if (client != 0) {
- client->decStrong(clazz);
- env->SetIntField(clazz, sso.client, 0);
- }
-}
-
-static void SurfaceSession_kill(JNIEnv* env, jobject clazz)
-{
- SurfaceComposerClient* client =
- (SurfaceComposerClient*)env->GetIntField(clazz, sso.client);
- if (client != 0) {
- client->dispose();
- client->decStrong(clazz);
- env->SetIntField(clazz, sso.client, 0);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static sp<SurfaceControl> getSurfaceControl(JNIEnv* env, jobject clazz)
-{
- SurfaceControl* const p =
- (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
- return sp<SurfaceControl>(p);
-}
-
-static void setSurfaceControl(JNIEnv* env, jobject clazz,
- const sp<SurfaceControl>& surface)
-{
- SurfaceControl* const p =
- (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
- if (surface.get()) {
- surface->incStrong(clazz);
- }
- if (p) {
- p->decStrong(clazz);
- }
- env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
-}
-
-static sp<Surface> getSurface(JNIEnv* env, jobject clazz)
-{
- sp<Surface> result(Surface_getSurface(env, clazz));
- if (result == 0) {
- /*
- * if this method is called from the WindowManager's process, it means
- * the client is is not remote, and therefore is allowed to have
- * a Surface (data), so we create it here.
- * If we don't have a SurfaceControl, it means we're in a different
- * process.
- */
-
- SurfaceControl* const control =
- (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
- if (control) {
- result = control->getSurface();
- if (result != 0) {
- result->incStrong(clazz);
- env->SetIntField(clazz, so.surface, (int)result.get());
- }
- }
- }
- return result;
-}
-
-sp<ANativeWindow> android_Surface_getNativeWindow(
- JNIEnv* env, jobject clazz) {
- return getSurface(env, clazz);
-}
-
-bool android_Surface_isInstanceOf(JNIEnv* env, jobject obj) {
- jclass surfaceClass = env->FindClass(kSurfaceClassPathName);
- return env->IsInstanceOf(obj, surfaceClass);
-}
-
-sp<Surface> Surface_getSurface(JNIEnv* env, jobject clazz) {
- sp<Surface> surface((Surface*)env->GetIntField(clazz, so.surface));
- return surface;
-}
-
-void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface)
-{
- Surface* const p = (Surface*)env->GetIntField(clazz, so.surface);
- if (surface.get()) {
- surface->incStrong(clazz);
- }
- if (p) {
- p->decStrong(clazz);
- }
- env->SetIntField(clazz, so.surface, (int)surface.get());
- // This test is conservative and it would be better to compare the ISurfaces
- if (p && p != surface.get()) {
- jint generationId = env->GetIntField(clazz, so.surfaceGenerationId);
- generationId++;
- env->SetIntField(clazz, so.surfaceGenerationId, generationId);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static void Surface_init(
- JNIEnv* env, jobject clazz,
- jobject session,
- jint, jstring jname, jint layerStack, jint w, jint h, jint format, jint flags)
-{
- if (session == NULL) {
- doThrowNPE(env);
- return;
- }
-
- SurfaceComposerClient* client =
- (SurfaceComposerClient*)env->GetIntField(session, sso.client);
-
- sp<SurfaceControl> surface;
- if (jname == NULL) {
- surface = client->createSurface(layerStack, w, h, format, flags);
- } else {
- const jchar* str = env->GetStringCritical(jname, 0);
- const String8 name(str, env->GetStringLength(jname));
- env->ReleaseStringCritical(jname, str);
- surface = client->createSurface(name, layerStack, w, h, format, flags);
- }
-
- if (surface == 0) {
- jniThrowException(env, OutOfResourcesException, NULL);
- return;
- }
- setSurfaceControl(env, clazz, surface);
-}
-
-static void Surface_initFromSurfaceTexture(
- JNIEnv* env, jobject clazz, jobject jst)
-{
- sp<SurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, jst));
-
- if (st == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "SurfaceTexture has already been released");
- return;
- }
- sp<ISurfaceTexture> bq = st->getBufferQueue();
-
- sp<Surface> surface(new Surface(bq));
- if (surface == NULL) {
- jniThrowException(env, OutOfResourcesException, NULL);
- return;
- }
- setSurfaceControl(env, clazz, NULL);
- setSurface(env, clazz, surface);
-}
-
-static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
-{
- Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);
- if (parcel == NULL) {
- doThrowNPE(env);
- return;
- }
-
- sp<Surface> sur(Surface::readFromParcel(*parcel));
- setSurface(env, clazz, sur);
-}
-
-static jint Surface_getIdentity(JNIEnv* env, jobject clazz)
-{
- const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
- if (control != 0) return (jint) control->getIdentity();
- const sp<Surface>& surface(getSurface(env, clazz));
- if (surface != 0) return (jint) surface->getIdentity();
- return -1;
-}
-
-static void Surface_destroy(JNIEnv* env, jobject clazz, uintptr_t *ostack)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (SurfaceControl::isValid(surface)) {
- surface->clear();
- }
- setSurfaceControl(env, clazz, 0);
- setSurface(env, clazz, 0);
-}
-
-static void Surface_release(JNIEnv* env, jobject clazz, uintptr_t *ostack)
-{
- setSurfaceControl(env, clazz, 0);
- setSurface(env, clazz, 0);
-}
-
-static jboolean Surface_isValid(JNIEnv* env, jobject clazz)
-{
- const sp<SurfaceControl>& surfaceControl(getSurfaceControl(env, clazz));
- if (surfaceControl != 0) {
- return SurfaceControl::isValid(surfaceControl) ? JNI_TRUE : JNI_FALSE;
- }
- const sp<Surface>& surface(getSurface(env, clazz));
- return Surface::isValid(surface) ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean Surface_isConsumerRunningBehind(JNIEnv* env, jobject clazz)
-{
- int value = 0;
- const sp<Surface>& surface(getSurface(env, clazz));
- if (!Surface::isValid(surface)) {
- doThrowIAE(env);
- return 0;
- }
- ANativeWindow* anw = static_cast<ANativeWindow *>(surface.get());
- anw->query(anw, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value);
- return (jboolean)value;
-}
-
-static inline SkBitmap::Config convertPixelFormat(PixelFormat format)
-{
- /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
- we can map to SkBitmap::kARGB_8888_Config, and optionally call
- bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator)
- */
- switch (format) {
- case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config;
- case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config;
- case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config;
- case PIXEL_FORMAT_A_8: return SkBitmap::kA8_Config;
- default: return SkBitmap::kNo_Config;
- }
-}
-
-static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
-{
- const sp<Surface>& surface(getSurface(env, clazz));
- if (!Surface::isValid(surface)) {
- doThrowIAE(env);
- return 0;
- }
-
- // get dirty region
- Region dirtyRegion;
- if (dirtyRect) {
- Rect dirty;
- dirty.left = env->GetIntField(dirtyRect, ro.l);
- dirty.top = env->GetIntField(dirtyRect, ro.t);
- dirty.right = env->GetIntField(dirtyRect, ro.r);
- dirty.bottom= env->GetIntField(dirtyRect, ro.b);
- if (!dirty.isEmpty()) {
- dirtyRegion.set(dirty);
- }
- } else {
- dirtyRegion.set(Rect(0x3FFF,0x3FFF));
- }
-
- Surface::SurfaceInfo info;
- status_t err = surface->lock(&info, &dirtyRegion);
- if (err < 0) {
- const char* const exception = (err == NO_MEMORY) ?
- OutOfResourcesException :
- "java/lang/IllegalArgumentException";
- jniThrowException(env, exception, NULL);
- return 0;
- }
-
- // Associate a SkCanvas object to this surface
- jobject canvas = env->GetObjectField(clazz, so.canvas);
- env->SetIntField(canvas, co.surfaceFormat, info.format);
-
- SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
- SkBitmap bitmap;
- ssize_t bpr = info.s * bytesPerPixel(info.format);
- bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
- if (info.format == PIXEL_FORMAT_RGBX_8888) {
- bitmap.setIsOpaque(true);
- }
- if (info.w > 0 && info.h > 0) {
- bitmap.setPixels(info.bits);
- } else {
- // be safe with an empty bitmap.
- bitmap.setPixels(NULL);
- }
- nativeCanvas->setBitmapDevice(bitmap);
-
- SkRegion clipReg;
- if (dirtyRegion.isRect()) { // very common case
- const Rect b(dirtyRegion.getBounds());
- clipReg.setRect(b.left, b.top, b.right, b.bottom);
- } else {
- size_t count;
- Rect const* r = dirtyRegion.getArray(&count);
- while (count) {
- clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
- r++, count--;
- }
- }
-
- nativeCanvas->clipRegion(clipReg);
-
- int saveCount = nativeCanvas->save();
- env->SetIntField(clazz, so.saveCount, saveCount);
-
- if (dirtyRect) {
- const Rect& bounds(dirtyRegion.getBounds());
- env->SetIntField(dirtyRect, ro.l, bounds.left);
- env->SetIntField(dirtyRect, ro.t, bounds.top);
- env->SetIntField(dirtyRect, ro.r, bounds.right);
- env->SetIntField(dirtyRect, ro.b, bounds.bottom);
- }
-
- return canvas;
-}
-
-static void Surface_unlockCanvasAndPost(
- JNIEnv* env, jobject clazz, jobject argCanvas)
-{
- jobject canvas = env->GetObjectField(clazz, so.canvas);
- if (env->IsSameObject(canvas, argCanvas) == JNI_FALSE) {
- doThrowIAE(env);
- return;
- }
-
- const sp<Surface>& surface(getSurface(env, clazz));
- if (!Surface::isValid(surface))
- return;
-
- // detach the canvas from the surface
- SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
- int saveCount = env->GetIntField(clazz, so.saveCount);
- nativeCanvas->restoreToCount(saveCount);
- nativeCanvas->setBitmapDevice(SkBitmap());
- env->SetIntField(clazz, so.saveCount, 0);
-
- // unlock surface
- status_t err = surface->unlockAndPost();
- if (err < 0) {
- doThrowIAE(env);
- }
-}
-
-static void Surface_unlockCanvas(
- JNIEnv* env, jobject clazz, jobject argCanvas)
-{
- // XXX: this API has been removed
- doThrowIAE(env);
-}
-
-static void Surface_openTransaction(
- JNIEnv* env, jobject clazz)
-{
- SurfaceComposerClient::openGlobalTransaction();
-}
-
-static void Surface_closeTransaction(
- JNIEnv* env, jobject clazz)
-{
- SurfaceComposerClient::closeGlobalTransaction();
-}
-
-static void Surface_setOrientation(
- JNIEnv* env, jobject clazz, jint display, jint orientation)
-{
- int err = SurfaceComposerClient::setOrientation(display, orientation, 0);
- if (err < 0) {
- doThrowIAE(env);
- }
-}
class ScreenshotPixelRef : public SkPixelRef {
public:
@@ -488,16 +97,18 @@
SkSafeRef(ctable);
setImmutable();
}
+
virtual ~ScreenshotPixelRef() {
SkSafeUnref(fCTable);
}
- status_t update(int width, int height, int minLayer, int maxLayer, bool allLayers) {
+ status_t update(const sp<IBinder>& display, int width, int height,
+ int minLayer, int maxLayer, bool allLayers) {
status_t res = (width > 0 && height > 0)
? (allLayers
- ? mScreenshot.update(width, height)
- : mScreenshot.update(width, height, minLayer, maxLayer))
- : mScreenshot.update();
+ ? mScreenshot.update(display, width, height)
+ : mScreenshot.update(display, width, height, minLayer, maxLayer))
+ : mScreenshot.update(display);
if (res != NO_ERROR) {
return res;
}
@@ -538,13 +149,311 @@
typedef SkPixelRef INHERITED;
};
-static jobject doScreenshot(JNIEnv* env, jobject clazz, jint width, jint height,
- jint minLayer, jint maxLayer, bool allLayers)
-{
+
+// ----------------------------------------------------------------------------
+
+static sp<SurfaceControl> getSurfaceControl(JNIEnv* env, jobject surfaceObj) {
+ return reinterpret_cast<SurfaceControl*>(
+ env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl));
+}
+
+static void setSurfaceControl(JNIEnv* env, jobject surfaceObj,
+ const sp<SurfaceControl>& surface) {
+ SurfaceControl* const p = reinterpret_cast<SurfaceControl*>(
+ env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl));
+ if (surface.get()) {
+ surface->incStrong(surfaceObj);
+ }
+ if (p) {
+ p->decStrong(surfaceObj);
+ }
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl,
+ reinterpret_cast<jint>(surface.get()));
+}
+
+static sp<Surface> getSurface(JNIEnv* env, jobject surfaceObj) {
+ sp<Surface> result(android_view_Surface_getSurface(env, surfaceObj));
+ if (result == NULL) {
+ /*
+ * if this method is called from the WindowManager's process, it means
+ * the client is is not remote, and therefore is allowed to have
+ * a Surface (data), so we create it here.
+ * If we don't have a SurfaceControl, it means we're in a different
+ * process.
+ */
+
+ SurfaceControl* const control = reinterpret_cast<SurfaceControl*>(
+ env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurfaceControl));
+ if (control) {
+ result = control->getSurface();
+ if (result != NULL) {
+ result->incStrong(surfaceObj);
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface,
+ reinterpret_cast<jint>(result.get()));
+ }
+ }
+ }
+ return result;
+}
+
+sp<ANativeWindow> android_view_Surface_getNativeWindow(JNIEnv* env, jobject surfaceObj) {
+ return getSurface(env, surfaceObj);
+}
+
+bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj) {
+ return env->IsInstanceOf(obj, gSurfaceClassInfo.clazz);
+}
+
+sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) {
+ return reinterpret_cast<Surface*>(
+ env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface));
+}
+
+static void setSurface(JNIEnv* env, jobject surfaceObj, const sp<Surface>& surface) {
+ Surface* const p = reinterpret_cast<Surface*>(
+ env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface));
+ if (surface.get()) {
+ surface->incStrong(surfaceObj);
+ }
+ if (p) {
+ p->decStrong(surfaceObj);
+ }
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mNativeSurface,
+ reinterpret_cast<jint>(surface.get()));
+
+ // This test is conservative and it would be better to compare the ISurfaces
+ if (p && p != surface.get()) {
+ jint generationId = env->GetIntField(surfaceObj,
+ gSurfaceClassInfo.mGenerationId);
+ generationId++;
+ env->SetIntField(surfaceObj,
+ gSurfaceClassInfo.mGenerationId, generationId);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static void nativeCreate(JNIEnv* env, jobject surfaceObj, jobject sessionObj,
+ jstring nameStr, jint w, jint h, jint format, jint flags) {
+ ScopedUtfChars name(env, nameStr);
+ sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
+
+ sp<SurfaceControl> surface = client->createSurface(
+ String8(name.c_str()), w, h, format, flags);
+ if (surface == NULL) {
+ jniThrowException(env, OutOfResourcesException, NULL);
+ return;
+ }
+
+ setSurfaceControl(env, surfaceObj, surface);
+}
+
+static void nativeCreateFromSurfaceTexture(JNIEnv* env, jobject surfaceObj,
+ jobject surfaceTextureObj) {
+ sp<SurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj));
+ if (st == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "SurfaceTexture has already been released");
+ return;
+ }
+
+ sp<ISurfaceTexture> bq = st->getBufferQueue();
+
+ sp<Surface> surface(new Surface(bq));
+ if (surface == NULL) {
+ jniThrowException(env, OutOfResourcesException, NULL);
+ return;
+ }
+
+ setSurface(env, surfaceObj, surface);
+}
+
+static void nativeRelease(JNIEnv* env, jobject surfaceObj) {
+ setSurfaceControl(env, surfaceObj, NULL);
+ setSurface(env, surfaceObj, NULL);
+}
+
+static void nativeDestroy(JNIEnv* env, jobject surfaceObj) {
+ sp<SurfaceControl> surfaceControl(getSurfaceControl(env, surfaceObj));
+ if (SurfaceControl::isValid(surfaceControl)) {
+ surfaceControl->clear();
+ }
+ setSurfaceControl(env, surfaceObj, NULL);
+ setSurface(env, surfaceObj, NULL);
+}
+
+static jboolean nativeIsValid(JNIEnv* env, jobject surfaceObj) {
+ sp<SurfaceControl> surfaceControl(getSurfaceControl(env, surfaceObj));
+ if (surfaceControl != NULL) {
+ return SurfaceControl::isValid(surfaceControl) ? JNI_TRUE : JNI_FALSE;
+ }
+
+ sp<Surface> surface(getSurface(env, surfaceObj));
+ return Surface::isValid(surface) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint nativeGetIdentity(JNIEnv* env, jobject surfaceObj) {
+ sp<SurfaceControl> control(getSurfaceControl(env, surfaceObj));
+ if (control != NULL) {
+ return jint(control->getIdentity());
+ }
+
+ sp<Surface> surface(getSurface(env, surfaceObj));
+ if (surface != NULL) {
+ return jint(surface->getIdentity());
+ }
+
+ return -1;
+}
+
+static jboolean nativeIsConsumerRunningBehind(JNIEnv* env, jobject surfaceObj) {
+ sp<Surface> surface(getSurface(env, surfaceObj));
+ if (!Surface::isValid(surface)) {
+ doThrowIAE(env);
+ return JNI_FALSE;
+ }
+
+ int value = 0;
+ ANativeWindow* anw = static_cast<ANativeWindow*>(surface.get());
+ anw->query(anw, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value);
+ return value;
+}
+
+static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
+ /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
+ we can map to SkBitmap::kARGB_8888_Config, and optionally call
+ bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator)
+ */
+ switch (format) {
+ case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config;
+ case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config;
+ case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config;
+ case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config;
+ case PIXEL_FORMAT_A_8: return SkBitmap::kA8_Config;
+ default: return SkBitmap::kNo_Config;
+ }
+}
+
+static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jobject dirtyRectObj) {
+ sp<Surface> surface(getSurface(env, surfaceObj));
+ if (!Surface::isValid(surface)) {
+ doThrowIAE(env);
+ return NULL;
+ }
+
+ // get dirty region
+ Region dirtyRegion;
+ if (dirtyRectObj) {
+ Rect dirty;
+ dirty.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
+ dirty.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
+ dirty.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
+ dirty.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
+ if (!dirty.isEmpty()) {
+ dirtyRegion.set(dirty);
+ }
+ } else {
+ dirtyRegion.set(Rect(0x3FFF, 0x3FFF));
+ }
+
+ Surface::SurfaceInfo info;
+ status_t err = surface->lock(&info, &dirtyRegion);
+ if (err < 0) {
+ const char* const exception = (err == NO_MEMORY) ?
+ OutOfResourcesException :
+ "java/lang/IllegalArgumentException";
+ jniThrowException(env, exception, NULL);
+ return NULL;
+ }
+
+ // Associate a SkCanvas object to this surface
+ jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
+ env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, info.format);
+
+ SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
+ env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
+ SkBitmap bitmap;
+ ssize_t bpr = info.s * bytesPerPixel(info.format);
+ bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
+ if (info.format == PIXEL_FORMAT_RGBX_8888) {
+ bitmap.setIsOpaque(true);
+ }
+ if (info.w > 0 && info.h > 0) {
+ bitmap.setPixels(info.bits);
+ } else {
+ // be safe with an empty bitmap.
+ bitmap.setPixels(NULL);
+ }
+ nativeCanvas->setBitmapDevice(bitmap);
+
+ SkRegion clipReg;
+ if (dirtyRegion.isRect()) { // very common case
+ const Rect b(dirtyRegion.getBounds());
+ clipReg.setRect(b.left, b.top, b.right, b.bottom);
+ } else {
+ size_t count;
+ Rect const* r = dirtyRegion.getArray(&count);
+ while (count) {
+ clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
+ r++, count--;
+ }
+ }
+
+ nativeCanvas->clipRegion(clipReg);
+
+ int saveCount = nativeCanvas->save();
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, saveCount);
+
+ if (dirtyRectObj) {
+ const Rect& bounds(dirtyRegion.getBounds());
+ env->SetIntField(dirtyRectObj, gRectClassInfo.left, bounds.left);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.top, bounds.top);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.right, bounds.right);
+ env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, bounds.bottom);
+ }
+
+ return canvasObj;
+}
+
+static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jobject canvasObj) {
+ jobject ownCanvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
+ if (!env->IsSameObject(ownCanvasObj, canvasObj)) {
+ doThrowIAE(env);
+ return;
+ }
+
+ sp<Surface> surface(getSurface(env, surfaceObj));
+ if (!Surface::isValid(surface)) {
+ return;
+ }
+
+ // detach the canvas from the surface
+ SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>(
+ env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
+ int saveCount = env->GetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount);
+ nativeCanvas->restoreToCount(saveCount);
+ nativeCanvas->setBitmapDevice(SkBitmap());
+ env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, 0);
+
+ // unlock surface
+ status_t err = surface->unlockAndPost();
+ if (err < 0) {
+ doThrowIAE(env);
+ }
+}
+
+static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
+ jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) {
+ sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
+ if (displayToken == NULL) {
+ return NULL;
+ }
+
ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL);
- if (pixels->update(width, height, minLayer, maxLayer, allLayers) != NO_ERROR) {
+ if (pixels->update(displayToken, width, height,
+ minLayer, maxLayer, allLayers) != NO_ERROR) {
delete pixels;
- return 0;
+ return NULL;
}
uint32_t w = pixels->getWidth();
@@ -571,94 +480,68 @@
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
}
-static jobject Surface_screenshotAll(JNIEnv* env, jobject clazz, jint width, jint height)
-{
- return doScreenshot(env, clazz, width, height, 0, 0, true);
+static void nativeOpenTransaction(JNIEnv* env, jclass clazz) {
+ SurfaceComposerClient::openGlobalTransaction();
}
-static jobject Surface_screenshot(JNIEnv* env, jobject clazz, jint width, jint height,
- jint minLayer, jint maxLayer)
-{
- return doScreenshot(env, clazz, width, height, minLayer, maxLayer, false);
+static void nativeCloseTransaction(JNIEnv* env, jclass clazz) {
+ SurfaceComposerClient::closeGlobalTransaction();
}
-static void Surface_setLayer(
- JNIEnv* env, jobject clazz, jint zorder)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
+static void nativeSetLayer(JNIEnv* env, jobject surfaceObj, jint zorder) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
+
status_t err = surface->setLayer(zorder);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setPosition(
- JNIEnv* env, jobject clazz, jfloat x, jfloat y)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
+static void nativeSetPosition(JNIEnv* env, jobject surfaceObj, jfloat x, jfloat y) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
+
status_t err = surface->setPosition(x, y);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setSize(
- JNIEnv* env, jobject clazz, jint w, jint h)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
+static void nativeSetSize(JNIEnv* env, jobject surfaceObj, jint w, jint h) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
+
status_t err = surface->setSize(w, h);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_hide(
- JNIEnv* env, jobject clazz)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
- status_t err = surface->hide();
- if (err<0 && err!=NO_INIT) {
- doThrowIAE(env);
- }
-}
+static void nativeSetFlags(JNIEnv* env, jobject surfaceObj, jint flags, jint mask) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
-static void Surface_show(
- JNIEnv* env, jobject clazz)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
- status_t err = surface->show();
- if (err<0 && err!=NO_INIT) {
- doThrowIAE(env);
- }
-}
-
-static void Surface_setFlags(
- JNIEnv* env, jobject clazz, jint flags, jint mask)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
status_t err = surface->setFlags(flags, mask);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setTransparentRegion(
- JNIEnv* env, jobject clazz, jobject argRegion)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
- SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region);
+static void nativeSetTransparentRegionHint(JNIEnv* env, jobject surfaceObj, jobject regionObj) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
- const SkIRect& b(nativeRegion->getBounds());
+ SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
+ if (!region) {
+ doThrowIAE(env);
+ return;
+ }
+
+ const SkIRect& b(region->getBounds());
Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
- if (nativeRegion->isComplex()) {
- SkRegion::Iterator it(*nativeRegion);
+ if (region->isComplex()) {
+ SkRegion::Iterator it(*region);
while (!it.done()) {
const SkIRect& r(it.rect());
reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom);
@@ -667,130 +550,183 @@
}
status_t err = surface->setTransparentRegionHint(reg);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setAlpha(
- JNIEnv* env, jobject clazz, jfloat alpha)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
+static void nativeSetAlpha(JNIEnv* env, jobject surfaceObj, jfloat alpha) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
+
status_t err = surface->setAlpha(alpha);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setMatrix(
- JNIEnv* env, jobject clazz,
- jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
- if (surface == 0) return;
+static void nativeSetMatrix(JNIEnv* env, jobject surfaceObj,
+ jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
+
status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy);
- if (err<0 && err!=NO_INIT) {
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setWindowCrop(JNIEnv* env, jobject thiz, jobject crop)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, thiz));
- if (surface == 0) return;
+static void nativeSetWindowCrop(JNIEnv* env, jobject surfaceObj, jobject cropObj) {
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
- Rect nativeCrop;
- if (crop) {
- nativeCrop.left = env->GetIntField(crop, ro.l);
- nativeCrop.top = env->GetIntField(crop, ro.t);
- nativeCrop.right = env->GetIntField(crop, ro.r);
- nativeCrop.bottom= env->GetIntField(crop, ro.b);
+ Rect crop;
+ if (cropObj) {
+ crop.left = env->GetIntField(cropObj, gRectClassInfo.left);
+ crop.top = env->GetIntField(cropObj, gRectClassInfo.top);
+ crop.right = env->GetIntField(cropObj, gRectClassInfo.right);
+ crop.bottom = env->GetIntField(cropObj, gRectClassInfo.bottom);
} else {
- nativeCrop.left = nativeCrop.top = nativeCrop.right =
- nativeCrop.bottom = 0;
+ crop.left = crop.top = crop.right = crop.bottom = 0;
}
- status_t err = surface->setCrop(nativeCrop);
- if (err<0 && err!=NO_INIT) {
+ status_t err = surface->setCrop(crop);
+ if (err < 0 && err != NO_INIT) {
doThrowIAE(env);
}
}
-static void Surface_setLayerStack(JNIEnv* env, jobject thiz, jint layerStack)
-{
- const sp<SurfaceControl>& surface(getSurfaceControl(env, thiz));
- if (surface == 0) return;
+static void nativeSetLayerStack(JNIEnv* env, jobject surfaceObj, jint layerStack) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ if (surface == NULL) return;
- // TODO(mathias): Everything.
+ status_t err = surface->setLayerStack(layerStack);
+ if (err < 0 && err != NO_INIT) {
+ doThrowIAE(env);
+ }
+}
+
+static jobject nativeGetBuiltInDisplay(JNIEnv* env, jclass clazz, jint id) {
+ sp<IBinder> token(SurfaceComposerClient::getBuiltInDisplay(id));
+ return javaObjectForIBinder(env, token);
+}
+
+static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj) {
+ ScopedUtfChars name(env, nameObj);
+ // TODO: pass the name to SF.
+ sp<IBinder> token(SurfaceComposerClient::createDisplay());
+ return javaObjectForIBinder(env, token);
+}
+
+static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jobject surfaceTextureObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ if (!surfaceTextureObj) {
+ SurfaceComposerClient::setDisplaySurface(token, NULL);
+ return;
+ }
+
+ sp<SurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj));
+ if (st == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "SurfaceTexture has already been released");
+ return;
+ }
+
+ sp<ISurfaceTexture> bq = st->getBufferQueue();
+ SurfaceComposerClient::setDisplaySurface(token, bq);
+}
+
+static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jint layerStack) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setDisplayLayerStack(token, layerStack);
+}
+
+static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jint orientation, jobject rect1Obj, jobject rect2Obj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ Rect rect1;
+ rect1.left = env->GetIntField(rect1Obj, gRectClassInfo.left);
+ rect1.top = env->GetIntField(rect1Obj, gRectClassInfo.top);
+ rect1.right = env->GetIntField(rect1Obj, gRectClassInfo.right);
+ rect1.bottom = env->GetIntField(rect1Obj, gRectClassInfo.bottom);
+
+ Rect rect2;
+ rect2.left = env->GetIntField(rect2Obj, gRectClassInfo.left);
+ rect2.top = env->GetIntField(rect2Obj, gRectClassInfo.top);
+ rect2.right = env->GetIntField(rect2Obj, gRectClassInfo.right);
+ rect2.bottom = env->GetIntField(rect2Obj, gRectClassInfo.bottom);
+
+ SurfaceComposerClient::setDisplayProjection(token, orientation, rect1, rect2);
+}
+
+static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jobject infoObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return JNI_FALSE;
+
+ DisplayInfo info;
+ if (SurfaceComposerClient::getDisplayInfo(token, &info)) {
+ return JNI_FALSE;
+ }
+
+ env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w);
+ env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h);
+ env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps);
+ env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density);
+ env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
+ env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
+ return JNI_TRUE;
}
// ----------------------------------------------------------------------------
-static void Surface_copyFrom(
- JNIEnv* env, jobject clazz, jobject other)
-{
- if (clazz == other)
- return;
-
- if (other == NULL) {
- doThrowNPE(env);
- return;
- }
-
+static void nativeCopyFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) {
/*
* This is used by the WindowManagerService just after constructing
* a Surface and is necessary for returning the Surface reference to
* the caller. At this point, we should only have a SurfaceControl.
*/
- const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
- const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
- if (!SurfaceControl::isSameSurface(surface, rhs)) {
+ sp<SurfaceControl> surface(getSurfaceControl(env, surfaceObj));
+ sp<SurfaceControl> other(getSurfaceControl(env, otherObj));
+ if (!SurfaceControl::isSameSurface(surface, other)) {
// we reassign the surface only if it's a different one
// otherwise we would loose our client-side state.
- setSurfaceControl(env, clazz, rhs);
+ setSurfaceControl(env, surfaceObj, other);
}
}
-static void Surface_transferFrom(
- JNIEnv* env, jobject clazz, jobject other)
-{
- if (clazz == other)
- return;
-
- if (other == NULL) {
- doThrowNPE(env);
- return;
- }
-
- sp<SurfaceControl> control(getSurfaceControl(env, other));
- sp<Surface> surface(Surface_getSurface(env, other));
- setSurfaceControl(env, clazz, control);
- setSurface(env, clazz, surface);
- setSurfaceControl(env, other, 0);
- setSurface(env, other, 0);
+static void nativeTransferFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) {
+ sp<SurfaceControl> control(getSurfaceControl(env, otherObj));
+ sp<Surface> surface(android_view_Surface_getSurface(env, otherObj));
+ setSurfaceControl(env, surfaceObj, control);
+ setSurface(env, surfaceObj, surface);
+ setSurfaceControl(env, otherObj, NULL);
+ setSurface(env, otherObj, NULL);
}
-static void Surface_readFromParcel(
- JNIEnv* env, jobject clazz, jobject argParcel)
-{
- Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
+static void nativeReadFromParcel(JNIEnv* env, jobject surfaceObj, jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
doThrowNPE(env);
return;
}
- sp<Surface> sur(Surface::readFromParcel(*parcel));
- setSurface(env, clazz, sur);
+ sp<Surface> surface(Surface::readFromParcel(*parcel));
+ setSurfaceControl(env, surfaceObj, NULL);
+ setSurface(env, surfaceObj, surface);
}
-static void Surface_writeToParcel(
- JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
-{
- Parcel* parcel = (Parcel*)env->GetIntField(
- argParcel, no.native_parcel);
-
+static void nativeWriteToParcel(JNIEnv* env, jobject surfaceObj, jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
if (parcel == NULL) {
doThrowNPE(env);
return;
@@ -802,110 +738,121 @@
// available we let it parcel itself. Finally, if the Surface is also
// NULL we fall back to using the SurfaceControl path which sends an
// empty surface; this matches legacy behavior.
- const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
+ sp<SurfaceControl> control(getSurfaceControl(env, surfaceObj));
if (control != NULL) {
SurfaceControl::writeSurfaceToParcel(control, parcel);
} else {
- sp<Surface> surface(Surface_getSurface(env, clazz));
+ sp<Surface> surface(android_view_Surface_getSurface(env, surfaceObj));
if (surface != NULL) {
Surface::writeToParcel(surface, parcel);
} else {
SurfaceControl::writeSurfaceToParcel(NULL, parcel);
}
}
- if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
- setSurfaceControl(env, clazz, NULL);
- setSurface(env, clazz, NULL);
- }
}
// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-
-static void nativeClassInit(JNIEnv* env, jclass clazz);
-
-static JNINativeMethod gSurfaceSessionMethods[] = {
- {"init", "()V", (void*)SurfaceSession_init },
- {"destroy", "()V", (void*)SurfaceSession_destroy },
- {"kill", "()V", (void*)SurfaceSession_kill },
-};
static JNINativeMethod gSurfaceMethods[] = {
- {"nativeClassInit", "()V", (void*)nativeClassInit },
- {"init", "(Landroid/view/SurfaceSession;ILjava/lang/String;IIIII)V", (void*)Surface_init },
- {"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel },
- {"initFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V", (void*)Surface_initFromSurfaceTexture },
- {"getIdentity", "()I", (void*)Surface_getIdentity },
- {"destroy", "()V", (void*)Surface_destroy },
- {"release", "()V", (void*)Surface_release },
- {"copyFrom", "(Landroid/view/Surface;)V", (void*)Surface_copyFrom },
- {"transferFrom", "(Landroid/view/Surface;)V", (void*)Surface_transferFrom },
- {"isValid", "()Z", (void*)Surface_isValid },
- {"lockCanvasNative", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;", (void*)Surface_lockCanvas },
- {"unlockCanvasAndPost", "(Landroid/graphics/Canvas;)V", (void*)Surface_unlockCanvasAndPost },
- {"unlockCanvas", "(Landroid/graphics/Canvas;)V", (void*)Surface_unlockCanvas },
- {"openTransaction", "()V", (void*)Surface_openTransaction },
- {"closeTransaction", "()V", (void*)Surface_closeTransaction },
- {"setOrientation", "(II)V", (void*)Surface_setOrientation },
- {"screenshot", "(II)Landroid/graphics/Bitmap;", (void*)Surface_screenshotAll },
- {"screenshot", "(IIII)Landroid/graphics/Bitmap;", (void*)Surface_screenshot },
- {"setLayer", "(I)V", (void*)Surface_setLayer },
- {"setPosition", "(FF)V",(void*)Surface_setPosition },
- {"setSize", "(II)V",(void*)Surface_setSize },
- {"hide", "()V", (void*)Surface_hide },
- {"show", "()V", (void*)Surface_show },
- {"setFlags", "(II)V",(void*)Surface_setFlags },
- {"setTransparentRegionHint","(Landroid/graphics/Region;)V", (void*)Surface_setTransparentRegion },
- {"setAlpha", "(F)V", (void*)Surface_setAlpha },
- {"setMatrix", "(FFFF)V", (void*)Surface_setMatrix },
- {"readFromParcel", "(Landroid/os/Parcel;)V", (void*)Surface_readFromParcel },
- {"writeToParcel", "(Landroid/os/Parcel;I)V", (void*)Surface_writeToParcel },
- {"isConsumerRunningBehind", "()Z", (void*)Surface_isConsumerRunningBehind },
- {"setWindowCrop", "(Landroid/graphics/Rect;)V", (void*)Surface_setWindowCrop },
- {"setLayerStack", "(I)V", (void*)Surface_setLayerStack },
+ {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIII)V",
+ (void*)nativeCreate },
+ {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V",
+ (void*)nativeCreateFromSurfaceTexture },
+ {"nativeRelease", "()V",
+ (void*)nativeRelease },
+ {"nativeDestroy", "()V",
+ (void*)nativeDestroy },
+ {"nativeIsValid", "()Z",
+ (void*)nativeIsValid },
+ {"nativeGetIdentity", "()I",
+ (void*)nativeGetIdentity },
+ {"nativeIsConsumerRunningBehind", "()Z",
+ (void*)nativeIsConsumerRunningBehind },
+ {"nativeLockCanvas", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;",
+ (void*)nativeLockCanvas },
+ {"nativeUnlockCanvasAndPost", "(Landroid/graphics/Canvas;)V",
+ (void*)nativeUnlockCanvasAndPost },
+ {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZ)Landroid/graphics/Bitmap;",
+ (void*)nativeScreenshot },
+ {"nativeOpenTransaction", "()V",
+ (void*)nativeOpenTransaction },
+ {"nativeCloseTransaction", "()V",
+ (void*)nativeCloseTransaction },
+ {"nativeSetLayer", "(I)V",
+ (void*)nativeSetLayer },
+ {"nativeSetPosition", "(FF)V",
+ (void*)nativeSetPosition },
+ {"nativeSetSize", "(II)V",
+ (void*)nativeSetSize },
+ {"nativeSetTransparentRegionHint", "(Landroid/graphics/Region;)V",
+ (void*)nativeSetTransparentRegionHint },
+ {"nativeSetAlpha", "(F)V",
+ (void*)nativeSetAlpha },
+ {"nativeSetMatrix", "(FFFF)V",
+ (void*)nativeSetMatrix },
+ {"nativeSetFlags", "(II)V",
+ (void*)nativeSetFlags },
+ {"nativeSetWindowCrop", "(Landroid/graphics/Rect;)V",
+ (void*)nativeSetWindowCrop },
+ {"nativeSetLayerStack", "(I)V",
+ (void*)nativeSetLayerStack },
+ {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
+ (void*)nativeGetBuiltInDisplay },
+ {"nativeCreateDisplay", "(Ljava/lang/String;)Landroid/os/IBinder;",
+ (void*)nativeCreateDisplay },
+ {"nativeSetDisplaySurface", "(Landroid/os/IBinder;Landroid/graphics/SurfaceTexture;)V",
+ (void*)nativeSetDisplaySurface },
+ {"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V",
+ (void*)nativeSetDisplayLayerStack },
+ {"nativeSetDisplayProjection", "(Landroid/os/IBinder;ILandroid/graphics/Rect;Landroid/graphics/Rect;)V",
+ (void*)nativeSetDisplayProjection },
+ {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z",
+ (void*)nativeGetDisplayInfo },
+ {"nativeCopyFrom", "(Landroid/view/Surface;)V",
+ (void*)nativeCopyFrom },
+ {"nativeTransferFrom", "(Landroid/view/Surface;)V",
+ (void*)nativeTransferFrom },
+ {"nativeReadFromParcel", "(Landroid/os/Parcel;)V",
+ (void*)nativeReadFromParcel },
+ {"nativeWriteToParcel", "(Landroid/os/Parcel;)V",
+ (void*)nativeWriteToParcel },
};
-void nativeClassInit(JNIEnv* env, jclass clazz)
-{
- so.surface = env->GetFieldID(clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I");
- so.surfaceGenerationId = env->GetFieldID(clazz, "mSurfaceGenerationId", "I");
- so.surfaceControl = env->GetFieldID(clazz, "mSurfaceControl", "I");
- so.saveCount = env->GetFieldID(clazz, "mSaveCount", "I");
- so.canvas = env->GetFieldID(clazz, "mCanvas", "Landroid/graphics/Canvas;");
-
- jclass surfaceSession = env->FindClass("android/view/SurfaceSession");
- sso.client = env->GetFieldID(surfaceSession, "mClient", "I");
-
- jclass canvas = env->FindClass("android/graphics/Canvas");
- no.native_canvas = env->GetFieldID(canvas, "mNativeCanvas", "I");
- co.surfaceFormat = env->GetFieldID(canvas, "mSurfaceFormat", "I");
-
- jclass region = env->FindClass("android/graphics/Region");
- no.native_region = env->GetFieldID(region, "mNativeRegion", "I");
-
- jclass parcel = env->FindClass("android/os/Parcel");
- no.native_parcel = env->GetFieldID(parcel, "mNativePtr", "I");
-
- jclass rect = env->FindClass("android/graphics/Rect");
- ro.l = env->GetFieldID(rect, "left", "I");
- ro.t = env->GetFieldID(rect, "top", "I");
- ro.r = env->GetFieldID(rect, "right", "I");
- ro.b = env->GetFieldID(rect, "bottom", "I");
-
- jclass point = env->FindClass("android/graphics/Point");
- po.x = env->GetFieldID(point, "x", "I");
- po.y = env->GetFieldID(point, "y", "I");
-}
-
int register_android_view_Surface(JNIEnv* env)
{
- int err;
- err = AndroidRuntime::registerNativeMethods(env, kSurfaceSessionClassPathName,
- gSurfaceSessionMethods, NELEM(gSurfaceSessionMethods));
-
- err |= AndroidRuntime::registerNativeMethods(env, kSurfaceClassPathName,
+ int err = AndroidRuntime::registerNativeMethods(env, "android/view/Surface",
gSurfaceMethods, NELEM(gSurfaceMethods));
+
+ jclass clazz = env->FindClass("android/view/Surface");
+ gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
+ gSurfaceClassInfo.mNativeSurface =
+ env->GetFieldID(gSurfaceClassInfo.clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I");
+ gSurfaceClassInfo.mNativeSurfaceControl =
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeSurfaceControl", "I");
+ gSurfaceClassInfo.mGenerationId =
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mGenerationId", "I");
+ gSurfaceClassInfo.mCanvas =
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;");
+ gSurfaceClassInfo.mCanvasSaveCount =
+ env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvasSaveCount", "I");
+
+ clazz = env->FindClass("android/graphics/Canvas");
+ gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
+ gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
+
+ clazz = env->FindClass("android/graphics/Rect");
+ gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
+ gRectClassInfo.top = env->GetFieldID(clazz, "top", "I");
+ gRectClassInfo.right = env->GetFieldID(clazz, "right", "I");
+ gRectClassInfo.bottom = env->GetFieldID(clazz, "bottom", "I");
+
+ clazz = env->FindClass("android/view/Surface$PhysicalDisplayInfo");
+ gPhysicalDisplayInfoClassInfo.width = env->GetFieldID(clazz, "width", "I");
+ gPhysicalDisplayInfoClassInfo.height = env->GetFieldID(clazz, "height", "I");
+ gPhysicalDisplayInfoClassInfo.refreshRate = env->GetFieldID(clazz, "refreshRate", "F");
+ gPhysicalDisplayInfoClassInfo.density = env->GetFieldID(clazz, "density", "F");
+ gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
+ gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
return err;
}
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
new file mode 100644
index 0000000..1494bc5
--- /dev/null
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "SurfaceSession"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_SurfaceSession.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+
+static struct {
+ jfieldID mNativeClient;
+} gSurfaceSessionClassInfo;
+
+
+sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
+ JNIEnv* env, jobject surfaceSessionObj) {
+ return reinterpret_cast<SurfaceComposerClient*>(
+ env->GetIntField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
+}
+
+
+static jint nativeCreate(JNIEnv* env, jclass clazz) {
+ SurfaceComposerClient* client = new SurfaceComposerClient();
+ client->incStrong(clazz);
+ return reinterpret_cast<jint>(client);
+}
+
+static void nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
+ SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
+ client->decStrong(clazz);
+}
+
+static void nativeKill(JNIEnv* env, jclass clazz, jint ptr) {
+ SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
+ client->dispose();
+}
+
+
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeCreate", "()I",
+ (void*)nativeCreate },
+ { "nativeDestroy", "(I)V",
+ (void*)nativeDestroy },
+ { "nativeKill", "(I)V",
+ (void*)nativeKill }
+};
+
+int register_android_view_SurfaceSession(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/SurfaceSession",
+ gMethods, NELEM(gMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass clazz = env->FindClass("android/view/SurfaceSession");
+ gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 9c6c7de..f8904bd 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -326,7 +326,7 @@
return 0;
}
- window = android_Surface_getNativeWindow(_env, native_window);
+ window = android_view_Surface_getNativeWindow(_env, native_window);
if (window == NULL)
goto not_valid_surface;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1c9b440..23a412f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -43,6 +43,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
<protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
<protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
<protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
@@ -62,6 +63,7 @@
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_STOPPED" />
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
@@ -724,6 +726,13 @@
android:description="@string/permdesc_mediaStorageWrite"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to access all multi-user external storage @hide -->
+ <permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+ android:label="@string/permlab_sdcardAccessAll"
+ android:description="@string/permdesc_sdcardAccessAll"
+ android:protectionLevel="signature" />
+
<!-- ============================================ -->
<!-- Permissions for low-level system interaction -->
<!-- ============================================ -->
@@ -1276,6 +1285,12 @@
android:description="@string/permdesc_retrieve_window_info"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to magnify the content of a display. -->
+ <permission android:name="android.permission.MAGNIFY_DISPLAY"
+ android:label="@string/permlab_magnify_display"
+ android:description="@string/permdesc_magnify_display"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to watch and control how activities are
started globally in the system. Only for is in debugging
(usually the monkey command). -->
@@ -1662,7 +1677,6 @@
<!-- Package verifier needs to have this permission before the PackageManager will
trust it to verify packages.
- @hide
-->
<permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
android:label="@string/permlab_packageVerificationAgent"
diff --git a/core/res/res/anim/keyguard_security_animate_in.xml b/core/res/res/anim/keyguard_security_animate_in.xml
new file mode 100644
index 0000000..4ee30c3
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_animate_in.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+
+ <scale
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:fromXScale="0.0"
+ android:toXScale="1.0"
+ android:fromYScale="1.0"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillAfter="true"
+ android:duration="@integer/kg_security_flip_duration"
+ android:startOffset="@integer/kg_security_flip_duration" />
+
+</set>
+
diff --git a/core/res/res/anim/keyguard_security_animate_out.xml b/core/res/res/anim/keyguard_security_animate_out.xml
new file mode 100644
index 0000000..76d065c
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_animate_out.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false">
+
+ <scale
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromXScale="1.0"
+ android:toXScale="0.0"
+ android:fromYScale="1.0"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillAfter="true"
+ android:duration="@integer/kg_security_flip_duration" />
+
+</set>
+
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
new file mode 100644
index 0000000..7d5516a
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_fade_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@interpolator/decelerate_quad"
+ android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@integer/kg_security_fade_duration" />
+
+
diff --git a/core/res/res/anim/keyguard_security_fade_out.xml b/core/res/res/anim/keyguard_security_fade_out.xml
new file mode 100644
index 0000000..caf896e
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_fade_out.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@integer/kg_security_fade_duration"
+/>
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
index 13ab8f7..3b64f47 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
index 13ab8f7..3b64f47 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
index 1d76bb5..0437c31 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
index 8ebd761..6a2a92c 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
index 8ebd761..6a2a92c 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
index b405d81..882ed61 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
index fee599a..b9266a6 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
index fee599a..b9266a6 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
index dddfc26..dbcede7 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
index ab40fa7..42fc83c 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
index 8077921..42fc83c 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
index 0d8f8ba..958d023 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
index baf7018..3464f3d 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
index baf7018..3464f3d 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 7a24c9b..17acfc5 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 7a24c9b..17acfc5 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
index 93c6d1b..9b8ca22 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
index 93c6d1b..9b8ca22 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
index 120f963..bc20f6c 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
index 120f963..bc20f6c 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
index 6b106fb..571819b 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
index 6b106fb..571819b 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
index a1b7003..677069a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
index a1b7003..677069a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 7176a6b..1f83b5a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 7176a6b..1f83b5a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
index 7fba6a5..733cf45 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
index 7fba6a5..733cf45 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
index 8bbfe9f..2265de4 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
index 8bbfe9f..2265de4 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
index 28f0ee6..f3ada58 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
index 28f0ee6..f3ada58 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
index c4c41a3..6b5fa5a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
index c4c41a3..6b5fa5a 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
index 256067d..9eaf9d5 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
index 2338175..55a125e 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
index 79e56f5..13205f0 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
index e029f21..6f5dcc1 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
index 8ee0072..be3f7a1 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
index df030c1..2e92a6d 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
index 50534a1..0e5444b 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
index 0b84155..32ca205 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
index 4d3d208..e83b346 100644
--- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
index 924a99d..fd4fbf8 100644
--- a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_dark.9.png
index 53871a0..8aee55a 100644
--- a/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_light.9.png
index e3a0313..2ebb7a2 100644
--- a/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/menu_hardkey_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png
index eb28ff9..88f8765 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
index d281adb..fa68a13 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png
index b298586..78c63cb 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
index 4215396..e13a9f8 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png
index a280eab..26d2e16 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
index f8d619b..00ae92a 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png
index 955a2f3..dc20a8d 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
index 6c22e22..272a2a1 100644
--- a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png
index 34a88df..78e583c 100644
--- a/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png
index b03842d..fb54f22 100644
--- a/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png
index 2d306d9..210832c 100644
--- a/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png
index 720c417..d0d9419 100644
--- a/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png
index b038fba..be365ec 100644
--- a/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png
index ccffda9..cd7b803 100644
--- a/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png
index f638d5e..84560c5 100644
--- a/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png
index 0aedd25..e101d50 100644
--- a/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png
index 56481ce..b7c5dcd 100644
--- a/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png
index 56481ce..1aaa9ae 100644
--- a/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
index d15f9a6..e6ef296 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
index 073f91e..7368261 100644
--- a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
index fe75315..29e33e3 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
index d7f78ab..b70db4e 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
index 769bc0a..73ec37c 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
index 312a0f4..a77d66d 100644
--- a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
index 56481ce..3141caf 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
index 56481ce..df7f770 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
index e27b409..ab381a6 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
index 073f91e..ed1306c 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
index f33763b..269e6b3 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
index d7f78ab..e0a83fe 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
index 2e50f72..9f14a06 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
index 312a0f4..c458698 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
index 6784032..180d85c 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
index 74746cb..e075149 100644
--- a/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
index 38d00db..87933fa 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
index 38d00db..87933fa 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
index 85c2c4f..77f6492 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
index 4a6351a..d424a0e 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
index 4a6351a..d424a0e 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
index 89d7a0e..683f128 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
index 39950f6..0763868 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
index 39950f6..0763868 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
index 0895d57..0e0da34 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
index 54c6354..accc761 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
index 50070ed..accc761 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
index 6a1b6a1..eb7a1fd 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
index 13a1fdd..7e4eb5e 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
index 13a1fdd..7e4eb5e 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 88da06e..3fdd3bc 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 88da06e..3fdd3bc 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
index ae2c2c4..eaa02b3 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
index ae2c2c4..eaa02b3 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
index db0f9ab..28c8b94 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
index db0f9ab..28c8b94 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
index 7abaf3e..6090cce 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
index 7abaf3e..6090cce 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
index 354fd0e..045dc9a 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
index 354fd0e..045dc9a 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index d311c80..3f2e982 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index d311c80..3f2e982 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
index d0fd585..14b958b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
index d0fd585..14b958b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
index e27b3de..4db22d4 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
index e27b3de..4db22d4 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
index cbed62f..a11e1c7 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
index cbed62f..a11e1c7 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
index 16fa332..6c4aa16 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
index 16fa332..6c4aa16 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
index 611d538..f874d66 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
index cf2f01b..0d6c715 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
index fb3660e..63144ae 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
index f18050e..953ba78 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
index b620341..0c57ffc 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
index 4035428..c6be52e 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
index 4d99748..7e9f258 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
index 6f5f149..11cc5a4 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
index 460ec46..9583c9b 100644
--- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
index e84adf2..54d2cd0 100644
--- a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_dark.9.png
index 9d80b77..ce48b33 100644
--- a/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_light.9.png
index efa4325..1f313af 100644
--- a/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/menu_hardkey_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png
index 29aff4d..8d75946 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
index 4055f70..716560b 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png
index ea4ee04..c3ba89c 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
index f74c02b..67c5358 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png
index 09a2992..c015f43 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
index 6536ee6..487edc2 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png
index 202b5b7..2fa15e7 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
index 6de0ba8..a964b22 100644
--- a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png
index 48af192..5ac84dd 100644
--- a/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png
index b3180cb..3eeebc4 100644
--- a/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png
index 22eddd8..2734f20 100644
--- a/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png
index dad0ec9..a78d6c0 100644
--- a/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png
index 2cdd273..7d91915 100644
--- a/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png
index f605db8..d7c4b87 100644
--- a/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png
index a699924..b82d1ac 100644
--- a/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png
index f3c12d7..f9b5f64 100644
--- a/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png
index b69c3f0..33f798d 100644
--- a/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png
index b69c3f0..622c684 100644
--- a/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
index 6b4bba0..82fea5e 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
index 3d8898e..c780d7d 100644
--- a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png
index e922f71..24bdf71 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png
index 3b92894..0d5ea83 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
index 7e44919..709f5ef 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
index 09b5616..ea6d2f7 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png
index efbaef8..2d8dd23 100644
--- a/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png
index efbaef8..2d8dd23 100644
--- a/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png
index b69c3f0..371d6e9 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png
index b69c3f0..225317f 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
index 6b4bba0..4bd6f9f 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
index 3d8898e..4b837b0 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png
index e922f71..51cf919 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png
index 3b92894..af8d7e1 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
index 7e44919..d0fb869 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
index 09b5616..a0e233e 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png
index 2a78e8c..2ed4985 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png
index 632d9fc..0603348 100644
--- a/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/textfield_multiline_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
new file mode 100644
index 0000000..c30eb1c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
new file mode 100644
index 0000000..e5d5771
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/magnified_region_frame.9.png b/core/res/res/drawable-nodpi/magnified_region_frame.9.png
new file mode 100644
index 0000000..4cadefb
--- /dev/null
+++ b/core/res/res/drawable-nodpi/magnified_region_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
index b7707c6..d591bf8 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
index b7707c6..d591bf8 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
index 2ed6386..df2a621 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
index ffee5e2..b410d23 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
index ffee5e2..b410d23 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
index 702ebc6..392cee4 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
index 30bfa30..aed57c6 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
index 30bfa30..aed57c6 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
index 89ce2df..92a49db 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
index 745d53e..38f8c01 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
index c509934..38f8c01 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
index b1eab79..a0d64a3 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
index 417152b..930b5f2 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
index 417152b..930b5f2 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index c271216..c08deab 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index c271216..c08deab 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
index a2d3ecd..8b1a55c 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
index a2d3ecd..8b1a55c 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
index 80cbb05..77cd1fa 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
index 80cbb05..77cd1fa 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
index db2cfc5..e0e3540 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
index db2cfc5..e0e3540 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png
index 5086f46..9d16f32 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png
index 5086f46..9d16f32 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 0f5851b..324e490 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 0f5851b..324e490 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
index 74c853f..e126cc6 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
index 74c853f..e126cc6 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
index 7bd7af5..4c1f1b9 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
index 7bd7af5..4c1f1b9 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
index 71dad92..219d37b 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
index 71dad92..219d37b 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png
index 1f62eff..fde3ac3 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png
index 1f62eff..fde3ac3 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
index 94bb8e1..467ea1f 100644
--- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
index ef58e29..74929a3 100644
--- a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
index f4970ad..a8ab305 100644
--- a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
index 172fc3b..a8f02d6 100644
--- a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
index 2bab67a..97eb217 100644
--- a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
index 6b5f467..1300c19 100644
--- a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
index e1c602f..f82e26b 100644
--- a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
index 59db99c..8bd32a3 100644
--- a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
index e2aff72..f67e609 100644
--- a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png
index 93066c8..ed71eda 100644
--- a/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_dark.9.png
index 521e2d9..585bccc 100644
--- a/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_light.9.png
index 92e117d..a0669b9 100644
--- a/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/menu_hardkey_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png
index d8929fc..c43293d 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
index 9174c4e..3dc481e 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png
index 3015d30..9a7b173 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
index 126637d..6888fdc 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png
index d45c7a8..9408b47 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
index 29036b9..1cb95d1 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png
index 2cb34d7..a3c7711 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
index 82f752f..2a21210 100644
--- a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_default_holo_dark.9.png
index e94ce80..fadfb5d 100644
--- a/core/res/res/drawable-xhdpi/spinner_default_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_default_holo_light.9.png
index f006541..5a4ec3b 100644
--- a/core/res/res/drawable-xhdpi/spinner_default_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_disabled_holo_dark.9.png
index 7bfab99..7c3c49b 100644
--- a/core/res/res/drawable-xhdpi/spinner_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_disabled_holo_light.9.png
index 1edcc81..fe54126 100644
--- a/core/res/res/drawable-xhdpi/spinner_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_focused_holo_dark.9.png
index ff7b959..9ea957e 100644
--- a/core/res/res/drawable-xhdpi/spinner_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_focused_holo_light.9.png
index 156b5ab..8cb2fd8 100644
--- a/core/res/res/drawable-xhdpi/spinner_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_pressed_holo_dark.9.png
index d0ce6e4..aecf6bd 100644
--- a/core/res/res/drawable-xhdpi/spinner_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_pressed_holo_light.9.png
index 9e9617e..3273a22 100644
--- a/core/res/res/drawable-xhdpi/spinner_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_activated_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_activated_holo_dark.9.png
index b3f8cd6..653b7dc 100644
--- a/core/res/res/drawable-xhdpi/textfield_activated_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_activated_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_activated_holo_light.9.png
index b3f8cd6..08fcc5e 100644
--- a/core/res/res/drawable-xhdpi/textfield_activated_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_default_holo_dark.9.png
index b5c5907..3f63c3fc 100644
--- a/core/res/res/drawable-xhdpi/textfield_default_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_default_holo_light.9.png
index 30052e9..dbb9924 100644
--- a/core/res/res/drawable-xhdpi/textfield_default_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_dark.9.png
index 16be839..a9767fc 100644
--- a/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png
index ddd0559..40a28cf 100644
--- a/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_disabled_holo_dark.9.png
index 3d143b9..d78b10d 100644
--- a/core/res/res/drawable-xhdpi/textfield_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_disabled_holo_light.9.png
index dfb2185..4ffdd86 100644
--- a/core/res/res/drawable-xhdpi/textfield_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_dark.9.png
index b3f8cd6..e12da1b 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_light.9.png
index b3f8cd6..557788b 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_dark.9.png
index b5c5907..9a367c9 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_light.9.png
index 30052e9..147ac58 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_dark.9.png
index 16be839..f89316a 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_light.9.png
index ddd0559..06173a4 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_dark.9.png
index 3d143b9..1463e5d 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_light.9.png
index dfb2185..e1c7e8c 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_dark.9.png
index 7f0dc65..9247353 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_light.9.png
index 7f0dc65..cab8e9f 100644
--- a/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/textfield_multiline_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/progress_horizontal_holo_dark.xml b/core/res/res/drawable/progress_horizontal_holo_dark.xml
index ff270b3..bc1ecf3 100644
--- a/core/res/res/drawable/progress_horizontal_holo_dark.xml
+++ b/core/res/res/drawable/progress_horizontal_holo_dark.xml
@@ -21,11 +21,13 @@
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%"
+ android:scaleGravity="start"
android:drawable="@android:drawable/progress_secondary_holo_dark" />
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
+ android:scaleGravity="start"
android:drawable="@android:drawable/progress_primary_holo_dark" />
</item>
diff --git a/core/res/res/drawable/progress_horizontal_holo_light.xml b/core/res/res/drawable/progress_horizontal_holo_light.xml
index 4935185..ee9b629 100644
--- a/core/res/res/drawable/progress_horizontal_holo_light.xml
+++ b/core/res/res/drawable/progress_horizontal_holo_light.xml
@@ -21,11 +21,13 @@
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%"
+ android:scaleGravity="start"
android:drawable="@android:drawable/progress_secondary_holo_light" />
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
+ android:scaleGravity="start"
android:drawable="@android:drawable/progress_primary_holo_light" />
</item>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
index b117bb8..4d83191 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
@@ -19,10 +19,12 @@
android:drawable="@android:drawable/scrubber_track_holo_dark" />
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%"
- android:drawable="@android:drawable/scrubber_secondary_holo" />
+ android:scaleGravity="start"
+ android:drawable="@android:drawable/scrubber_secondary_holo" />
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
- android:drawable="@android:drawable/scrubber_primary_holo" />
+ android:scaleGravity="start"
+ android:drawable="@android:drawable/scrubber_primary_holo" />
</item>
</layer-list>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
index 6cd08ea..a3461fa 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
@@ -19,10 +19,12 @@
android:drawable="@android:drawable/scrubber_track_holo_light" />
<item android:id="@android:id/secondaryProgress">
<scale android:scaleWidth="100%"
- android:drawable="@android:drawable/scrubber_secondary_holo" />
+ android:scaleGravity="start"
+ android:drawable="@android:drawable/scrubber_secondary_holo" />
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
- android:drawable="@android:drawable/scrubber_primary_holo" />
+ android:scaleGravity="start"
+ android:drawable="@android:drawable/scrubber_primary_holo" />
</item>
</layer-list>
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
new file mode 100644
index 0000000..b404155
--- /dev/null
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -0,0 +1,58 @@
+<?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.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.internal.policy.impl.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+ android:id="@+id/app_widget_container"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone">
+
+ <!-- TODO: Remove this once supported as a widget -->
+ <include layout="@layout/keyguard_status_view"/>
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
+
+
+ <ViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_selector_view"/>
+ <include layout="@layout/keyguard_account_view"/>
+ <include layout="@layout/keyguard_pattern_view"/>
+ <include layout="@layout/keyguard_password_view"/>
+ <include layout="@layout/keyguard_sim_pin_view"/>
+ <include layout="@layout/keyguard_sim_puk_view"/>
+ <include layout="@layout/keyguard_face_unlock_view"/>
+
+ </ViewFlipper>
+
+</com.android.internal.policy.impl.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
new file mode 100644
index 0000000..5dc2225
--- /dev/null
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -0,0 +1,47 @@
+<?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.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.internal.policy.impl.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_host_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:clipChildren="false">
+
+ <ViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_selector_view"/>
+ <include layout="@layout/keyguard_account_view"/>
+ <include layout="@layout/keyguard_pattern_view"/>
+ <include layout="@layout/keyguard_password_view"/>
+ <include layout="@layout/keyguard_sim_pin_view"/>
+ <include layout="@layout/keyguard_sim_puk_view"/>
+ <include layout="@layout/keyguard_face_unlock_view"/>
+
+ </ViewFlipper>
+
+</com.android.internal.policy.impl.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
new file mode 100644
index 0000000..e77f584
--- /dev/null
+++ b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
@@ -0,0 +1,67 @@
+<?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.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.internal.policy.impl.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+ android:id="@+id/app_widget_container"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone">
+
+ <!-- TODO: Remove this once supported as a widget -->
+ <include layout="@layout/keyguard_status_view"/>
+
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
+
+ <FrameLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <ViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="@dimen/kg_security_view_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <include layout="@layout/keyguard_selector_view"/>
+ <include layout="@layout/keyguard_account_view"/>
+ <include layout="@layout/keyguard_pattern_view"/>
+ <include layout="@layout/keyguard_password_view"/>
+ <include layout="@layout/keyguard_sim_pin_view"/>
+ <include layout="@layout/keyguard_sim_puk_view"/>
+ <include layout="@layout/keyguard_face_unlock_view"/>
+
+ </ViewFlipper>
+
+ </FrameLayout>
+
+</com.android.internal.policy.impl.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
new file mode 100644
index 0000000..082f481
--- /dev/null
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -0,0 +1,61 @@
+<?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.
+*/
+-->
+
+<!-- This is the host view that generally contains two sub views: the widget view
+ and the security view. -->
+<com.android.internal.policy.impl.keyguard.KeyguardHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_host_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:clipChildren="false">
+
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="0.4"
+ android:visibility="gone">
+
+ <!-- TODO: Remove this once supported as a widget -->
+ <include layout="@layout/keyguard_status_view"/>
+
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
+
+ <ViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="@dimen/kg_security_view_width"
+ android:layout_height="0dip"
+ android:layout_weight="0.6"
+ android:layout_gravity="center">
+
+ <include layout="@layout/keyguard_selector_view"/>
+ <include layout="@layout/keyguard_account_view"/>
+ <include layout="@layout/keyguard_pattern_view"/>
+ <include layout="@layout/keyguard_password_view"/>
+ <include layout="@layout/keyguard_sim_pin_view"/>
+ <include layout="@layout/keyguard_sim_puk_view"/>
+ <include layout="@layout/keyguard_face_unlock_view"/>
+
+ </ViewFlipper>
+
+</com.android.internal.policy.impl.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
index 4ceb907..4f94f96 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
@@ -154,7 +154,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="530dip"
android:layout_height="530dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
index da627b5..9a649fb 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
@@ -157,7 +157,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="440dip"
android:layout_height="440dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
index df999f0..c6ddd1b 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
index 565785b..765dc95 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
index 51b946a..a71ef30 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
@@ -126,7 +126,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="530dip"
android:layout_height="530dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
index 1d6b6a9..0c4a2c7 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
@@ -119,7 +119,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="440dip"
android:layout_height="440dip"
diff --git a/core/res/res/layout/keyguard_account_view.xml b/core/res/res/layout/keyguard_account_view.xml
new file mode 100644
index 0000000..481f0c1
--- /dev/null
+++ b/core/res/res/layout/keyguard_account_view.xml
@@ -0,0 +1,72 @@
+<?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.
+*/
+-->
+<com.android.internal.policy.impl.keyguard.KeyguardAccountView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_account_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <EditText
+ android:id="@+id/login"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="7dip"
+ android:layout_marginEnd="7dip"
+ android:layout_alignParentTop="true"
+ android:hint="@string/kg_login_username_hint"
+ android:inputType="textEmailAddress"
+ />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/login"
+ android:layout_marginTop="15dip"
+ android:layout_marginStart="7dip"
+ android:layout_marginEnd="7dip"
+ android:inputType="textPassword"
+ android:hint="@string/kg_login_password_hint"
+ android:nextFocusRight="@+id/ok"
+ android:nextFocusDown="@+id/ok"
+ />
+
+ <!-- ok below password, aligned to right of screen -->
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="7dip"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentBottom="true"
+ android:text="@string/kg_login_submit_button"
+ />
+
+ </RelativeLayout>
+
+</com.android.internal.policy.impl.keyguard.KeyguardAccountView>
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
new file mode 100644
index 0000000..60f0492
--- /dev/null
+++ b/core/res/res/layout/keyguard_face_unlock_view.xml
@@ -0,0 +1,61 @@
+<?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.
+*/
+-->
+
+<!-- This is the screen that allows the user to unlock by showing their face. -->
+<com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_face_unlock_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+
+ <RelativeLayout
+ android:id="@+id/face_unlock_area_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/face_unlock_height"
+ android:background="@drawable/intro_bg"
+ android:gravity="center">
+
+ <View
+ android:id="@+id/spotlightMask"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/facelock_spotlight_mask"
+ />
+
+ <ImageView
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dip"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true"
+ android:src="@drawable/ic_facial_backup"
+ />
+
+ </RelativeLayout>
+
+</com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView>
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
new file mode 100644
index 0000000..9999177
--- /dev/null
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="125dp"
+ android:layout_height="125dp"
+ android:background="#550000ff"
+ android:gravity="center_horizontal">
+ <ImageView
+ android:id="@+id/keyguard_user_avatar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"/>
+ <TextView
+ android:id="@+id/keyguard_user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:textSize="12sp"
+ android:background="#99FFFFFF"
+ android:textColor="#ff000000"/>
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
new file mode 100644
index 0000000..3ed9103
--- /dev/null
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="375dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+
+ <include
+ android:id="@+id/keyguard_active_user"
+ android:layout_width="250dp"
+ android:layout_height="250dp"
+ layout="@layout/keyguard_multi_user_avatar"/>
+
+ <ScrollView
+ android:layout_width="125dp"
+ android:layout_height="250dp">
+ <LinearLayout
+ android:id="@+id/keyguard_inactive_users"
+ android:orientation="vertical"
+ layout_width="match_parent"
+ layout_height="wrap_content"/>
+ </ScrollView>
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_multi_user_selector_widget.xml b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
new file mode 100644
index 0000000..c00089c
--- /dev/null
+++ b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/keyguard_multi_user_selector"/>
+
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_navigation.xml b/core/res/res/layout/keyguard_navigation.xml
new file mode 100644
index 0000000..a033101
--- /dev/null
+++ b/core/res/res/layout/keyguard_navigation.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="left">
+
+ <LinearLayout
+ android:id="@+id/keyguard_click_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="?android:attr/buttonBarButtonStyle"
+ android:padding="10dip"
+ android:clickable="true">
+
+ <ImageView
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <!-- message area for security screen -->
+ <TextView
+ android:id="@+id/keyguard_message_area"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="end"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:layout_marginEnd="6dip"
+ android:layout_marginStart="6dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_password_view.xml b/core/res/res/layout/keyguard_password_view.xml
new file mode 100644
index 0000000..4ea471e
--- /dev/null
+++ b/core/res/res/layout/keyguard_password_view.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<com.android.internal.policy.impl.keyguard.KeyguardPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_password_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <!-- Password entry field -->
+ <!-- Note: the entire container is styled to look like the edit field,
+ since the backspace/IME switcher looks better inside -->
+ <LinearLayout
+ android:layout_gravity="center_vertical|fill_horizontal"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:background="@*android:drawable/lockscreen_password_field_dark"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip">
+
+ <EditText android:id="@+id/passwordEntry"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_horizontal"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:textStyle="normal"
+ android:inputType="textPassword"
+ android:textSize="36sp"
+ android:background="@null"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+
+ <!-- This delete button is only visible for numeric PIN entry -->
+ <ImageButton android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@*android:drawable/ic_input_delete"
+ android:clickable="true"
+ android:padding="8dip"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+
+ <ImageView android:id="@+id/switch_ime_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@*android:drawable/ic_lockscreen_ime"
+ android:clickable="true"
+ android:padding="8dip"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+
+ </LinearLayout>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="match_parent"
+ android:layout_marginStart="4dip"
+ android:layout_marginEnd="4dip"
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip"
+ android:background="#40000000"
+ android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
+ android:visibility="gone"
+ android:clickable="true"
+ />
+
+</com.android.internal.policy.impl.keyguard.KeyguardPasswordView>
diff --git a/core/res/res/layout/keyguard_pattern_view.xml b/core/res/res/layout/keyguard_pattern_view.xml
new file mode 100644
index 0000000..954a92c
--- /dev/null
+++ b/core/res/res/layout/keyguard_pattern_view.xml
@@ -0,0 +1,64 @@
+<?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.
+*/
+-->
+
+<!-- This is the screen that shows the 9 circle unlock widget and instructs
+ the user how to unlock their device, or make an emergency call. This
+ is the portrait layout. -->
+<com.android.internal.policy.impl.keyguard.KeyguardPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_pattern_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <GridLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <Space android:layout_gravity="fill" />
+
+ <Button android:id="@+id/forgot_password_button"
+ android:layout_gravity="right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawableLeft="@*android:drawable/lockscreen_forgot_password_button"
+ android:drawablePadding="0dip"
+ android:visibility="gone"/>
+
+ <!-- We need MATCH_PARENT here only to force the size of the parent to be passed to
+ the pattern view for it to compute its size. This is an unusual case, caused by
+ LockPatternView's requirement to maintain a square aspect ratio based on the width
+ of the screen. -->
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="8dip"
+ android:layout_marginBottom="4dip"
+ android:layout_marginStart="8dip"
+ android:layout_gravity="center_horizontal"
+ />
+
+ </GridLayout>
+
+</com.android.internal.policy.impl.keyguard.KeyguardPatternView>
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 71fb363..e0a3ce3 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -209,7 +209,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="0"
android:layout_column="2"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 486c7fe..0212f73 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -200,7 +200,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="3"
android:layout_column="0"
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index edd74e2..27f6629 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -162,7 +162,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="0"
android:layout_column="1"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index f20cbcd..de94451 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -171,7 +171,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="4"
android:layout_column="0"
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
new file mode 100644
index 0000000..d7f98f9
--- /dev/null
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -0,0 +1,82 @@
+<?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.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.policy.impl.keyguard.KeyguardSelectorView
+ xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_selector_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:visibility="gone">
+ <!-- TODO: Remove this when supported as a widget -->
+ <include layout="@layout/keyguard_status_view"/>
+ </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:gravity="center">
+
+ <com.android.internal.widget.multiwaveview.GlowPadView
+ android:id="@+id/glow_pad_view"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+
+ prvandroid:targetDrawables="@*android:array/lockscreen_targets_with_camera"
+ prvandroid:targetDescriptions="@*android:array/lockscreen_target_descriptions_with_camera"
+ prvandroid:directionDescriptions="@*android:array/lockscreen_direction_descriptions"
+ prvandroid:handleDrawable="@*android:drawable/ic_lockscreen_handle"
+ prvandroid:outerRingDrawable="@*android:drawable/ic_lockscreen_outerring"
+ prvandroid:outerRadius="@*android:dimen/glowpadview_target_placement_radius"
+ prvandroid:innerRadius="@*android:dimen/glowpadview_inner_radius"
+ prvandroid:snapMargin="@*android:dimen/glowpadview_snap_margin"
+ prvandroid:feedbackCount="1"
+ prvandroid:vibrationDuration="20"
+ prvandroid:glowRadius="@*android:dimen/glowpadview_glow_radius"
+ prvandroid:pointDrawable="@*android:drawable/ic_lockscreen_glowdot"/>
+
+ <Button
+ android:id="@+id/emergency_call_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:drawableLeft="@*android:drawable/lockscreen_emergency_button"
+ android:text="@string/kg_emergency_call_label"
+ style="?android:attr/buttonBarButtonStyle"
+ android:drawablePadding="8dip"
+ android:layout_alignRight="@id/glow_pad_view"
+ android:layout_alignTop="@id/glow_pad_view"
+ />
+
+ </RelativeLayout>
+
+</com.android.internal.policy.impl.keyguard.KeyguardSelectorView>
+
diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml
new file mode 100644
index 0000000..122484a
--- /dev/null
+++ b/core/res/res/layout/keyguard_sim_pin_view.xml
@@ -0,0 +1,82 @@
+<?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.
+*/
+-->
+<!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. -->
+<com.android.internal.policy.impl.keyguard.KeyguardSimPinView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_sim_pin_view"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+
+ <!-- Password entry field -->
+ <!-- Note: the entire container is styled to look like the edit field,
+ since the backspace/IME switcher looks better inside -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginEnd="6dip"
+ android:layout_marginStart="6dip"
+ android:gravity="center_vertical"
+ android:background="@android:drawable/edit_text">
+
+ <!-- displays dots as user enters pin -->
+ <EditText android:id="@+id/sim_pin_entry"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:textColor="@*android:color/primary_text_holo_light"
+ android:textStyle="bold"
+ android:inputType="textPassword"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+
+ <ImageButton android:id="@+id/delete_button"
+ android:src="@android:drawable/ic_input_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="-3dip"
+ android:layout_marginBottom="-3dip"
+ />
+ </LinearLayout>
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="match_parent"
+ android:layout_marginStart="4dip"
+ android:layout_marginEnd="4dip"
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip"
+ android:background="#80ffffff"
+ android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
+ android:clickable="true"
+ />
+
+</com.android.internal.policy.impl.keyguard.KeyguardSimPinView>
diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml
new file mode 100644
index 0000000..8bb76c1
--- /dev/null
+++ b/core/res/res/layout/keyguard_sim_puk_view.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<com.android.internal.policy.impl.keyguard.KeyguardSimPukView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_sim_puk_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
+
+ <include layout="@layout/keyguard_navigation"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+
+ <LinearLayout android:id="@+id/topDisplayGroup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:paddingEnd="0dip"
+ android:layout_marginEnd="10dip"
+ android:layout_marginStart="10dip">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginEnd="6dip"
+ android:layout_marginStart="6dip"
+ android:gravity="center_vertical"
+ android:background="@*android:drawable/edit_text">
+
+ <!-- displays dots as user enters puk -->
+ <EditText android:id="@+id/sim_puk_entry"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:textStyle="bold"
+ android:inputType="textPassword"
+ android:textColor="#000"
+ android:hint="@string/kg_puk_enter_puk_hint"
+ />
+
+ <ImageButton android:id="@+id/puk_delete_button"
+ android:src="@*android:drawable/ic_input_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="-3dip"
+ android:layout_marginBottom="-3dip"
+ />
+
+ </LinearLayout>
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginEnd="6dip"
+ android:layout_marginStart="6dip"
+ android:gravity="center_vertical"
+ android:background="@*android:drawable/edit_text">
+
+ <!-- displays dots as user enters new pin -->
+ <EditText android:id="@+id/sim_pin_entry"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:textStyle="bold"
+ android:inputType="textPassword"
+ android:textColor="#000"
+ android:hint="@string/kg_puk_enter_pin_hint"
+ />
+
+ <ImageButton android:id="@+id/pin_delete_button"
+ android:src="@*android:drawable/ic_input_delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="-3dip"
+ android:layout_marginBottom="-3dip"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Numeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="match_parent"
+ android:layout_marginStart="4dip"
+ android:layout_marginEnd="4dip"
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip"
+ android:background="#80ffffff"
+ android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
+ android:clickable="true"
+ />
+
+</com.android.internal.policy.impl.keyguard.KeyguardSimPukView>
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
new file mode 100644
index 0000000..37e6779
--- /dev/null
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal">
+
+ <com.android.internal.policy.impl.keyguard.KeyguardStatusView
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
+
+ <com.android.internal.widget.DigitalClock android:id="@+id/time"
+ android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin"
+ android:layout_marginBottom="12dip"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:layout_gravity="end">
+
+ <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+ top of the other. Hence the redundant layout... -->
+ <TextView android:id="@*android:id/timeDisplayBackground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="6dip"
+ android:textColor="@*android:color/lockscreen_clock_background"
+ />
+
+ <TextView android:id="@*android:id/timeDisplayForeground"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="none"
+ android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="6dip"
+ android:textColor="@*android:color/lockscreen_clock_foreground"
+ android:layout_alignStart="@*android:id/timeDisplayBackground"
+ android:layout_alignTop="@*android:id/timeDisplayBackground"
+ />
+
+ </com.android.internal.widget.DigitalClock>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin">
+
+ <TextView
+ android:id="@*android:id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ />
+
+ <TextView
+ android:id="@*android:id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/status1"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ <TextView
+ android:id="@+id/owner_info"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ <TextView
+ android:id="@+id/carrier"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ />
+
+ </com.android.internal.policy.impl.keyguard.KeyguardStatusView>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/layout/overlay_display_window.xml b/core/res/res/layout/overlay_display_window.xml
new file mode 100644
index 0000000..36b4a0d
--- /dev/null
+++ b/core/res/res/layout/overlay_display_window.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#000000">
+ <TextureView android:id="@+id/overlay_display_window_texture"
+ android:layout_width="0px"
+ android:layout_height="0px" />
+ <TextView android:id="@+id/overlay_display_window_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal" />
+</FrameLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 560a6a8..01d440a 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"verander/vee uit interne mediabergingsinhoud"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Laat die program toe om die inhoud van die interne mediaberging te verander."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"verkry toegang tot alle gebruikers se eksterne berging"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Laat die program toe om toegang tot eksterne berging vir alle gebruikers te verkry."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"lees die kaslêerstelsel"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Laat die program toe om die kaslêerstelsel te lees en skryf."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"maak/ontvang internetoproepe"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Vereis dat gestoorde programdata geënkripteer word."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Deaktiveer kameras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Voorkom die gebruik van alle toestelkameras."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Tuis"</item>
<item msgid="869923650527136615">"Mobiel"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Gekoppel aan <xliff:g id="SESSION">%s</xliff:g>. Raak om die netwerk te bestuur."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Altydaan-VPN koppel tans..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altydaan-VPN gekoppel"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Altydaan-VPN-fout"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Raak om verbinding terug te stel"</string>
<string name="upload_file" msgid="2897957172366730416">"Kies lêer"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Geen lêer gekies nie"</string>
<string name="reset" msgid="2448168080964209908">"Stel terug"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-oudio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Klaar"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ingeboude skerm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ingeboude skerm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Oorlegger #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Noodoproep"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Het jy die patroon vergeet?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Verkeerde patroon"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Verkeerde wagwoord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Verkeerde PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer weer in <xliff:g id="NUMBER">%d</xliff:g> sekondes."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken jou patroon"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Voer SIM-PIN in"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Voer PIN in"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Voer wagwoord in"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-kode"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nuwe PIN-kode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ontsluit tans SIM-kaart…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Verkeerde PIN-kode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Tik \'n PIN in wat 4 tot 8 nommers lank is."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Tik \'n PUK in wat 8 nommers of meer is."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Voer PUK en nuwe PIN-kode in"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Jy het die verkeerde PUK getik."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Te veel patroonpogings"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Om te ontsluit, meld met jou Google-rekening aan."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Gebruikernaam (e-pos)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Wagwoord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Meld aan"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikernaam of wagwoord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Het jy jou gebruikernaam of wagwoord vergeet?"\n"Besoek"<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontroleer tans..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jy het jou PIN <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingetik. "\n\n"Probeer weer in <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer jou wagwoord verkeerdelik getik. "\n\n"Probeer weer in <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. "\n\n"Probeer weer in <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Na nog<xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die tablet na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Na nog<xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal die foon na die fabrieksverstek teruggestel word en al die gebruikerdata sal verlore wees."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die tablet te ontsluit. Die tablet sal nou na fabrieksverstek teruggestel word."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jy het <xliff:g id="NUMBER">%d</xliff:g> keer verkeerdelik gepoog om die foon te ontsluit. Die foon sal nou na fabrieksverstek teruggestel word."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur \'n e-posrekening te gebruik."\n\n" Probeer weer in <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur \'n e-posrekening te gebruik."\n\n" Probeer weer in <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index fc6d4b8..9d06464 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -225,7 +225,7 @@
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"በተለያዩ ተጠቃሚዎች መካከል መስተጋብር ለመፍጠር ሙሉ ፍቃድ"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"በተለያዩ ተጠቃሚዎች ላይ ሊኖሩ የሚችሉ መስተጋብሮችን ሁሉ ይፈቅዳል።"</string>
<string name="permlab_manageUsers" msgid="1676150911672282428">"ተጠቃሚዎችን ያስተዳድሩ"</string>
- <string name="permdesc_manageUsers" msgid="8409306667645355638">"መተግበሪያዎች በመሣሪያዎች ላይ ያሉ ተጠቃሚዎችን እንዲያቀናብር ያስችለዋል፣ መጠየቅን፣ መፍጠርንና መሰረዝን ጨምሮ።"</string>
+ <string name="permdesc_manageUsers" msgid="8409306667645355638">"መተግበሪያዎች በመሣሪያው ላይ ያሉ ተጠቃሚዎችን እንዲያቀናብር ያስችለዋል፣ መጠየቅን፣ መፍጠርንና መሰረዝን ጨምሮ።"</string>
<string name="permlab_getDetailedTasks" msgid="6229468674753529501">"እየሄዱ ስላሉ የመተግበሪያዎች ዝርዝሮች አምጣ"</string>
<string name="permdesc_getDetailedTasks" msgid="153824741440717599">"መተግበሪያው በአሁኑ ጊዜ እየተካሄዱ ስላሉና በቅርብ ጊዜ ስለተካሄዱ ተግባሮች መረጃ ዝርዝር እንዲያወጣ ይፈቅድለታል። ተንኮል-አዘል መተግበሪያዎች ስለ ሌሎች መተግበሪያዎች የግል መረጃ ሊያገኙ ይችላሉ።"</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"አሂድ ትግበራዎችን ድጋሚ ደርድር"</string>
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘቶችን ቀይር/ሰርዝ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘትን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"የሁሉም ተጠቃሚዎች ውጫዊ ማከማቻውን ይደርስበታል"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"መተግበሪያውን የሁሉም ተጠቃሚዎች ውጫዊ ማከማቻውን እንዲደርስ ይፈቅድለታል።"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"የመሸጎጫ ስርዓተ ፋይል ድረስ"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"መሸጎጫ ስርዓተ ፋይል ለማንበብ እና ለመፃፍ ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"የበይነ መረብ ጥሪዎች አድርግ/ተቀበል"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"የተከማቸ ትግበራ ውሂብ የተመሰጠረ እንዲሆን ጠይቅ።"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"ካሜራዎችን አቦዝን"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"የሁሉንም መሣሪያ ካሜራዎች መጠቀም ከልክል።"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"መነሻ"</item>
<item msgid="869923650527136615">"ተንቀሳቃሽ"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN በ<xliff:g id="APP">%s</xliff:g>ገብሯል"</string>
<string name="vpn_text" msgid="3011306607126450322">"አውታረመረብ ለማደራጀት ንካ።"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"ለ<xliff:g id="SESSION">%s</xliff:g>የተገናኘ። አውታረመረቡን ለማደራጀት ንካ።"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ሁልጊዜ የበራ VPN በመገናኘት ላይ…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ሁልጊዜ የበራ VPN ተገናኝቷል"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"ሁልጊዜ የበራ VPN ስህተት"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"ግንኙነት ዳግም ለማስጀመር ይንኩ"</string>
<string name="upload_file" msgid="2897957172366730416">"ፋይል ምረጥ"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"ምንም ፋይል አልተመረጠም"</string>
<string name="reset" msgid="2448168080964209908">"ዳግም አስጀምር"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"የብሉቱዝ ድምጽ"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"ተከናውኗል"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"የሚዲያ ውጽዓት"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"ውስጥ የተሰራ ማያ ገጽ"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"ውስጥ የተሰራ ማያ ገጽ"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"ተደራቢ #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"የአደጋ ጊዜ ጥሪ"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"ስርዓተ ጥለቱን እርሳ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"የተሳሳተ ስርዓተ ጥለት"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"የተሳሳተ ይለፍ ቃል"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"የተሳሳተ ፒን"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"በ<xliff:g id="NUMBER">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"ስርዓተ ጥለትዎን ይሳሉ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"የሲም ፒን ያስገቡ"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"ፒን ያስገቡ"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"የይለፍ ቃል ያስገቡ"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"የPUK ኮድ"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"አዲስ ፒን ኮድ"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"ሲም ካርዱን በመክፈት ላይ…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"ትክክል ያልሆነ ፒን ኮድ።"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ከ4 እስከ 8 ቁጥሮች የያዘ ፒን ይተይቡ"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"8 ወይም ከዚያ በላይ ቁጥሮችን የሆነ PUK ይተይቡ።"</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK እና አዲስ ፒን ይተይቡ"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"የተየቡት PUK ትክክል አይደለም።"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"በጣም ብዙ የስርዓተ ጥለት ሙከራዎች"</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="8849589033659332457">"በማረጋገጥ ላይ…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ፒንዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልተየቡም። "\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።"\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"የመክፈቻ ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። "\n\n" ከ<xliff:g id="NUMBER_1">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ጡባዊ ቱኮውን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ጡባዊ ቱኮው በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ስልኩን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ለመክፈት ሞክረዋል። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ ስልኩ በፋብሪካ ነባሪ ቅንብር ዳግም ይጀመርና ሁሉም ሁሉም የተጠቃሚ ውሂብ ይጠፋል።"</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d3b8f04..a9093a7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"للسماح للتطبيق بالكتابة إلى بطاقة SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تعديل/حذف محتويات وحدة تخزين الوسائط الداخلية"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"للسماح للتطبيق بتعديل محتويات وحدة تخزين الوسائط الداخلية."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"الدخول إلى سعة التخزين الخارجية لجميع المستخدمين"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"للسماح للتطبيق بالدخول إلى سعة التخزين الخارجية لجميع المستخدمين."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"الدخول إلى نظام ملفات ذاكرة التخزين المؤقت"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"للسماح للتطبيق بقراءة نظام ملفات ذاكرة التخزين المؤقت والكتابة به."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"إجراء/تلقي مكالمات عبر الإنترنت"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"يمكنك طلب تشفير بيانات التطبيق المخزنة."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"تعطيل الكاميرات"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"يمكنك منح استخدام جميع كاميرات الجهاز."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"الرئيسية"</item>
<item msgid="869923650527136615">"الجوال"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"تم الاتصال بـ <xliff:g id="SESSION">%s</xliff:g>. المس لإدارة الشبكة."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"جارٍ الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"تم الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطأ بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"المس لإعادة تعيين الاتصال"</string>
<string name="upload_file" msgid="2897957172366730416">"اختيار ملف"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"لم يتم اختيار أي ملف"</string>
<string name="reset" msgid="2448168080964209908">"إعادة تعيين"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"صوت بلوتوث"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"تم"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"المنفذ الإعلامي"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"شاشة مدمجة"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"شاشة مدمجة"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"المركب #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"الاتصال بالطوارئ"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"نسيت النقش"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"نقش خاطئ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"كلمة مرور خاطئة"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"رقم تعريف شخصي خاطئ"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"حاول مرة أخرى خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"ارسم نقشك"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"أدخل رقم التعريف الشخصي لبطاقة SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"أدخل رقم التعريف الشخصي"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"أدخل كلمة المرور"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"رمز PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"رمز رقم التعريف الشخصي الجديد"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"جارٍ إلغاء تأمين بطاقة SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"رقم التعريف الشخصي غير صحيح."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"اكتب رقم التعريف الشخصي المكون من 4 إلى 8 أرقام."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"اكتب رمز PUK المكون من 8 أرقام أو أكثر."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"اكتب رمز PUK ورمز رقم التعريف الشخصي الجديد"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"رمز PUK الذي كتبته غير صحيح."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"محاولات النقش كثيرة جدًا"</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="8849589033659332457">"جارٍ التحقق…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"لقد كتبت رقم التعريف الشخصي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"لقد حاولت إلغاء تأمين الجهاز اللوحي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الجهاز اللوحي على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"لقد حاولت إلغاء تأمين الهاتف بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستتم إعادة تعيين الهاتف على الإعدادات الافتراضية للمصنع وسيتم فقد جميع بيانات المستخدم."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d801628..cb25685 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дазваляе прыкладанням запісваць на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змяніць/выдаліць унутраныя носьбіты змесціва"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дазваляе прыкладанням змяняць змесціва ўнутранай памяці."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"доступ да знешніх захоўвання для ўсіх карыстальнікаў"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дазваляе ўсiм карыстальнiкам прыкладання атрымлiваць доступ да знешнега сховiшча"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"доступ да файлавай сістэмы кэша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дазваляе прыкладанню счытваць і выконваць запіс у файлавую сістэму кэш-памяці."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"рабіць/прымаць iнтэрнэт-выклікі"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Запыт на шыфраванне захаваных дадзеных прыкладанняў."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Адключыць камеры"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Забараніць выкарыстанне ўсіх камер прылады."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Галоўная старонка"</item>
<item msgid="869923650527136615">"Мабільны"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN актывуецца прыкладаннем <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Дакраніцеся, каб кіраваць сеткай."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Падлучаны да сеанса \"<xliff:g id="SESSION">%s</xliff:g>\". Дакраніцеся, каб кiраваць сеткай."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Падключэнне заўсёды ўключанага VPN..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Заўсёды ўключаны i падключаны VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Памылка заўсёды ўключанага VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Націсніце, каб скінуць падключэнне"</string>
<string name="upload_file" msgid="2897957172366730416">"Выберыце файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Файл не выбраны"</string>
<string name="reset" msgid="2448168080964209908">"Скінуць"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-аўдыё"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Гатова"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Мультымедыйны выхад"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Убудаваны экран"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Убудаваны экран"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Оверлей # <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Экстранны выклік"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забылі ключ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Няправільна ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Няправiльны пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Няправільны PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Паўтарыце спробу праз <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйце ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Увядзіце PIN-код SIM-карты"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Увядзіце PIN-код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Увядзіце пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Новы PIN-код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Разблакiроўка SIM-карты..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Няправільны PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Увядзіце PIN-код, які змяшчае ад 4 да 8 лічбаў."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Увядзіце PUK з 8 лічбаў ці больш."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Увядзіце PUK-код і новы PIN-код"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Няправільны PUK-код."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Занадта шмат спроб паўтарыць шаблон!"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Каб разблакiраваць, увайдзіце ў свой уліковы запіс 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">"Увайсцi"</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="8849589033659332457">"Праверка..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы няправільна ўвялі PIN-код пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы няправільна ўвялі пароль пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER_0">%d</xliff:g>). Пасля яшчэ некалькiх спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) ён будзе скінуты да заводскіх налад i карыстальнiцкiя дадзеныя будуць згубленыя."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER_0">%d</xliff:g>). Пасля яшчэ некалькiх спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) ён будзе скінуты да завадскіх налад i карыстальнiцкiя дадзеныя будуць згубленыя."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Вы няправільна спрабавалі разблакiраваць планшэт некалькi разоў (<xliff:g id="NUMBER">%d</xliff:g>). Цяпер ён будзе скінуты да завадскіх налад."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Вы няправільна спрабавалі разблакiраваць тэлефон некалькi разоў (<xliff:g id="NUMBER">%d</xliff:g>). Цяпер ён будзе скінуты да завадскіх налад."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 7e7c4e6..5deffde 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Разрешава на приложението да записва върху SD картата."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"пром./изтр. на съдърж. на вътр. мултим. хранил."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Разрешава на приложението да променя съдържанието на вътрешното мултимедийно хранилище."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"достъп до външ. хранилище за всички потребители"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Разрешава на приложението достъп до външното хранилище за всички потребители."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"достъп до файловата система на кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Разрешава на приложението да чете и записва във файловата система на кеша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"извършване/получаване на интернет обаждания"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Изисква съхраняваните данни за приложенията да бъдат шифровани."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Деактивиране на камерите"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Предотвратява употребата на камерите на всички устройства."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Домашен"</item>
<item msgid="869923650527136615">"Мобилен"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Свързана със: <xliff:g id="SESSION">%s</xliff:g>. Докоснете, за да управлявате мрежата."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Установява се връзка с винаги включената виртуална частна мрежа (VPN)…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Установена е връзка с винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка във винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Докоснете, за да възстановите връзката"</string>
<string name="upload_file" msgid="2897957172366730416">"Избор на файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Няма избран файл"</string>
<string name="reset" msgid="2448168080964209908">"Повторно задаване"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Звук през Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Изходяща мултимедия"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Вграден екран"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Вграден екран"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наслагване №<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Спешно обаждане"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забравена фигура"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Грешна фигура"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Грешна парола"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Грешен ПИН код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Опитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Начертайте фигурата си"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Въведете ПИН кода за SIM картата"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Въведете ПИН код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Въведете паролата"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK код"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Нов ПИН код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM картата се отключва…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неправилен ПИН код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Въведете ПИН код с четири до осем цифри."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Въведете PUK код с поне осем цифри."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Въведете PUK и новия ПИН код"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Въведеният от вас PUK код е неправилен."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Опитите за фигурата са твърде много"</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="8849589033659332457">"Проверява се…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Направихте опит да отключите неправилно таблета <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Направихте опит да отключите неправилно телефона <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдат възстановени стандартните му фабрични настройки и всички потребителски данни ще бъдат заличени."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0781217..c413943 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet a l\'aplicació escriure a la targeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Canvia/esborra emmagatz. intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet que l\'aplicació modifiqui el contingut de l\'emmagatzematge multimèdia intern."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"accedeix a l\'emmagatzematge extern per a tots els usuaris"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permet que l\'aplicació accedeixi a l\'emmagatzematge extern per a tots els usuaris."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accedir al sistema de fitxers de la memòria cau"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet que l\'aplicació llegeixi el sistema de fitxers de la memòria cau i que hi escrigui."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fer/rebre trucades per Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Requereix que les dades de l\'aplicació emmagatzemades estiguin encriptades."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desactiva les càmeres"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Impedeix l\'ús de totes les càmeres del dispositiu."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Mòbil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ha activat VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca per gestionar la xarxa."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Connectat a <xliff:g id="SESSION">%s</xliff:g>. Toca per gestionar la xarxa."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"T\'estàs connectant a la VPN sempre activada…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Estàs connectat a la VPN sempre activada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de la VPN sempre activada"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca per restablir la connexió"</string>
<string name="upload_file" msgid="2897957172366730416">"Trieu un fitxer"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"No s\'ha escollit cap fitxer"</string>
<string name="reset" msgid="2448168080964209908">"Reinicia"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Àudio per Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fet"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Sortida de contingut multimèdia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Pantalla integrada"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposa #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Trucada d\'emergència"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Patró oblidat"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patró incorrecte"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contrasenya incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecte"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuixa el patró"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introdueix el PIN de la SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introdueix el PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introdueix la contrasenya"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Codi PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Codi PIN nou"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"S\'està desbloquejant la targeta SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Codi PIN incorrecte."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Escriu un PIN que tingui de 4 a 8 números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Introdueix un PUK que tingui com a mínim 8 números."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Introdueix el codi PUK i el codi PIN nou"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"El PUK que has escrit no és correcte."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Massa intents incorrectes"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Per desbloquejar el telèfon, inicia la sessió amb el compte de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nom d\'usuari (correu electrònic)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contrasenya"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Inicia la sessió"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'usuari o contrasenya no vàlids."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Has oblidat el teu nom d\'usuari o la contrasenya?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"S\'està comprovant..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, la tauleta es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. D\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, el telèfon es restablirà a la configuració predeterminada de fàbrica i es perdran totes les dades dels usuaris."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has provat de desbloquejar la tauleta <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara la tauleta es restablirà a la configuració predeterminada de fàbrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has provat de desbloquejar el telèfon <xliff:g id="NUMBER">%d</xliff:g> vegades de manera incorrecta. Ara el telèfon es restablirà a la configuració predeterminada de fàbrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis la tauleta amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 50cd013..f1dca35 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikaci zapisovat na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Upravit/smazat interní úlož."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikaci upravovat obsah interního úložiště médií."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"přístup k externímu úložišti všech uživatelů"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Umožňuje aplikaci přistupovat k externímu úložišti pro všechny uživatele."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"přistupovat do souborového systému mezipaměti"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikaci číst a zapisovat do souborového systému mezipaměti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutečňovat a přijímat internetové hovory"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Požadovat šifrování uložených dat aplikací."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Vypnout fotoaparáty"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Zakázat používání všech fotoaparátů zařízení."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domů"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Připojeno k relaci <xliff:g id="SESSION">%s</xliff:g>. Dotykem můžete síť spravovat."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Připojování k trvalé síti VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Je připojena trvalá síť VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba trvalé sítě VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Klepnutím resetujete připojení"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvolit soubor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Není vybrán žádný soubor"</string>
<string name="reset" msgid="2448168080964209908">"Resetovat"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth Audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Hotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Integrovaná obrazovka"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integrovaná obrazovka"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Překryvná vrstva č. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Tísňové volání"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zapomenuté gesto"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávné gesto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávné heslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávný kód PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslete gesto"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadejte kód PIN SIM karty"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Zadejte kód PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Zadejte heslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Kód PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nový kód PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odblokování SIM karty..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nesprávný kód PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Zadejte kód PIN o délce 4–8 číslic."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Zadejte osmimístný nebo delší kód PUK."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Zadejte kód PUK a nový kód PIN."</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Zadali jste nesprávný kód PUK."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Příliš mnoho pokusů o nakreslení gesta"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Chcete-li telefon odemknout, přihlaste se pomocí svého účtu Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Uživatelské jméno (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Heslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Přihlásit se"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné uživatelské jméno nebo heslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zapomněli jste uživatelské jméno nebo heslo?"\n"Přejděte na stránku "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrola…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávný kód PIN. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali heslo. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v tabletu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Již jste se <xliff:g id="NUMBER_0">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech se v telefonu obnoví tovární nastavení a veškerá uživatelská data budou ztracena."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout tablet nesprávným způsobem. V tabletu se nyní obnoví výchozí tovární nastavení."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Již jste se <xliff:g id="NUMBER">%d</xliff:g>krát pokusili odemknout telefon nesprávným způsobem. V telefonu se nyní obnoví výchozí tovární nastavení."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g>dalších neúspěšných pokusech budete požádáni o odemčení tabletu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ea40879..2263665 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Rediger/slet internt medielagringsindhold"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillader, appen kan ændre indholdet af det interne medielager."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"få adgang til alle brugeres eksterne lagre"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillader, at appen får adgang til eksterne lagre for alle brugere."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"få adgang til cache-filsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillader, at appen kan læse og skrive i cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foretage/modtage internetopkald"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Kræver, at gemte appdata krypteres."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Deaktiver kameraer"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Bloker brug af alle kameraer på enheden."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hjem"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryk for at administrere netværket."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Forbundet til <xliff:g id="SESSION">%s</xliff:g>. Tryk for at administrere netværket."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til Always-on VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN er forbundet"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Tryk for at nulstille forbindelsen"</string>
<string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
<string name="reset" msgid="2448168080964209908">"Nulstil"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Udfør"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieudgang"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Indbygget skærm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Indbygget skærm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlejring nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nødopkald"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Glemt mønster"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Forkert mønster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Forkert adgangskode"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Forkert pinkode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn dit mønster"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Indtast pinkode til SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Indtast pinkode"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Angiv adgangskode"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-kode"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Ny pinkode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kortet låses op…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Forkert pinkode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Indtast en pinkode på mellem 4 og 8 tal."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Angiv en PUK-kode på 8 eller flere cifre."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Indtast PUK-koden og den nye pinkode"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Den indtastede PUK-kode er forkert."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"For mange forsøg på at tegne mønstret korrekt"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Lås op ved at logge ind med din Google-konto."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Brugernavn (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Adgangskode"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log ind"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldigt brugernavn eller ugyldig adgangskode."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt dit brugernavn eller din adgangskode?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrollerer..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg nulstilles tabletten til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg, nulstilles telefonen til fabriksindstillingerne, og alle brugerdata mistes."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har forsøgt at låse tabletten op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Tabletten nulstilles til fabriksindstillingerne."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har forsøgt at låse telefonen op forkert <xliff:g id="NUMBER">%d</xliff:g> gange. Telefonen nulstilles til fabriksindstillingerne."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg vil du blive bedt om at låse din tablet op ved hjælp af en e-mailkonto"\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto."\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a0280c8..2d01975 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ermöglicht der App, auf die SD-Karte zu schreiben"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Intern. Mediensp. änd./löschen"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ermöglicht der App, den Inhalt des internen Medienspeichers zu ändern"</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Auf externen Speicher aller Nutzer zugreifen"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Ermöglicht der App, auf externen Speicher aller Nutzer zuzugreifen."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Zugriff auf das Cache-Dateisystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ermöglicht der App Lese- und Schreibzugriff auf das Cache-Dateisystem"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Internetanrufe tätigen/annehmen"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Anforderung, dass gespeicherte App-Daten verschlüsselt werden"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Kameras deaktivieren"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Nutzung sämtlicher Gerätekameras unterbinden"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Privat"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wurde von <xliff:g id="APP">%s</xliff:g> aktiviert."</string>
<string name="vpn_text" msgid="3011306607126450322">"Zum Verwalten des Netzwerks berühren"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Verbunden mit <xliff:g id="SESSION">%s</xliff:g>. Zum Verwalten des Netzwerks berühren"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Verbindung zu durchgehend aktivem VPN wird hergestellt…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Mit durchgehend aktivem VPN verbunden"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Durchgehend aktives VPN – Verbindungsfehler"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Zum Zurücksetzen der Verbindung tippen"</string>
<string name="upload_file" msgid="2897957172366730416">"Datei auswählen"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Keine ausgewählt"</string>
<string name="reset" msgid="2448168080964209908">"Zurücksetzen"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-Audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fertig"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medienausgabe"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Integrierter Bildschirm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integrierter Bildschirm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay-Nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Notruf"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Muster vergessen"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Falsches Muster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Falsches Passwort"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Falsche PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Muster zeichnen"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM-PIN eingeben"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN eingeben"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Passwort eingeben"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-Code"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Neuer PIN-Code"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-Karte wird entsperrt…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Falscher PIN-Code"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Geben Sie eine 4- bis 8-stellige PIN ein."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Geben Sie eine mindestens 8-stellige PUK ein."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK und neuen PIN-Code eingeben"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Die von Ihnen eingegebene PUK ist nicht korrekt."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Zu viele Musterversuche"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Melden Sie sich zum Entsperren mit Ihrem Google-Konto an."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nutzername (E-Mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Passwort"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Anmelden"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ungültiger Nutzername oder ungültiges Passwort"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nutzernamen oder Passwort vergessen?"\n"Besuchen Sie "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Überprüfung…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Sie haben <xliff:g id="NUMBER_0">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 82f0c62..40ed1bd 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"τροπ./διαγ. περ. απ. εσ. μνήμ."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Επιτρέπει στην εφαρμογή να τροποποιήσει τα περιεχόμενα των εσωτερικών μέσων αποθήκευσης."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"πρόσβ.εξωτ.χωρ. αποθ. όλων των χρηστ."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Επιτρέπει στην εφαρμογή να αποκτήσει πρόσβαση σε εξωτερικό χώρο αποθήκευσης για όλους τους χρήστες."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"πρόσβαση στο σύστημα αρχείων προσωρινής μνήμης"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Επιτρέπει στην εφαρμογή την ανάγνωση και την εγγραφή του συστήματος αρχείων προσωρινής μνήμης."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"πραγματοποίηση/λήψη κλήσεων μέσω Διαδικτύου"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Να απαιτείται η κρυπτογράφηση των αποθηκευμένων δεδομένων εφαρμογής"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Απενεργοποίηση φωτογρ. μηχανών"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Να αποτρέπεται η χρήση των φωτογραφικών μηχανών της συσκευής."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Οικία"</item>
<item msgid="869923650527136615">"Κινητό"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Το VPN ενεργοποιήθηκε από την εφαρμογή <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Αγγίξτε για τη διαχείριση του δικτύου."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Συνδέθηκε με <xliff:g id="SESSION">%s</xliff:g>. Αγγίξτε για τη διαχείριση του δικτύου."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Σύνδεση πάντα ενεργοποιημένου VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Έχει συνδεθεί πάντα ενεργοποιημένο VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Σφάλμα πάντα ενεργοποιημένου VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Αγγίξτε για επαναφορά της σύνδεσης"</string>
<string name="upload_file" msgid="2897957172366730416">"Επιλογή αρχείου"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Δεν έχει επιλεγεί αρχείο"</string>
<string name="reset" msgid="2448168080964209908">"Επαναφορά"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ήχος Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Τέλος"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Έξοδος μέσων"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ενσωματωμένη οθόνη"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ενσωματωμένη οθόνη"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Επικάλυψη #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Κλήσεις επείγουσας ανάγκης"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Ξεχάσατε το μοτίβο"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Εσφαλμένο μοτίβο"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Εσφαλμένος κωδικός πρόσβασης"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Εσφαλμένος κωδικός PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Δοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Σχεδιάστε το μοτίβο σας"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Εισαγωγή PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Πληκτρολογήστε το PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Εισαγάγετε κωδικό πρόσβασης"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Κωδικός PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Νέος κωδικός PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ξεκλείδωμα κάρτας SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Εσφαλμένος κωδικός PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Πληκτρολογήστε έναν αριθμό PIN που να αποτελείται από 4 έως 8 αριθμούς."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Πληκτρολογήστε έναν κωδικό PUK με 8 αριθμούς ή περισσότερους."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Πληκτρολογήστε τον κωδικό PUK και τον νέο κωδικό PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Ο κωδικός PUK που πληκτρολογήσατε είναι εσφαλμένος."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Πάρα πολλές προσπάθειες μοτίβου"</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="8849589033659332457">"Έλεγχος…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Έχετε πληκτρολογήσει εσφαλμένα τον κωδικό σας PIN <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλετπα."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το τηλέφωνο <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες, το τηλέφωνο θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER">%d</xliff:g> φορές. Το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις."</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">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου."\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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4f7b087..65f2f77 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modify/delete internal media storage contents"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Allows the app to modify the contents of the internal media storage."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"access external storage of all users"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Allows the app to access external storage for all users."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access the cache file system"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Allows the app to read and write the cache file system."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"make/receive Internet calls"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Require that stored app data be encrypted."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Disable cameras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Prevent use of all device cameras."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Home"</item>
<item msgid="869923650527136615">"Mobile"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Connected to <xliff:g id="SESSION">%s</xliff:g>. Touch to manage the network."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN connecting…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN connected"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Always-on VPN error"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Touch to reset connection"</string>
<string name="upload_file" msgid="2897957172366730416">"Choose file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"No file chosen"</string>
<string name="reset" msgid="2448168080964209908">"Reset"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Done"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media output"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Built-in Screen"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Built-in Screen"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Emergency call"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Forgot Pattern"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Wrong Pattern"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Wrong Password"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Wrong PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Try again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Draw your pattern"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Enter SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Enter PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Enter Password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK code"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"New PIN code"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Unlocking SIM card…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Incorrect PIN code."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Type a PIN that is 4 to 8 numbers."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Type a PUK that is 8 numbers or longer."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Type PUK and new PIN code"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"The PUK you typed isn\'t correct."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Too many pattern attempts"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"To unlock, sign in with your Google account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Username (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Sign in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?"\n"Visit "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Checking…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the tablet will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, the phone will be reset to factory default and all user data will be lost."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"You have incorrectly attempted to unlock the tablet <xliff:g id="NUMBER">%d</xliff:g> times. The tablet will now be reset to factory default."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"You have incorrectly attempted to unlock the phone <xliff:g id="NUMBER">%d</xliff:g> times. The phone will now be reset to factory default."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your tablet using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 00d8617..14711ba 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -224,8 +224,8 @@
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permite que la aplicación lleve a cabo acciones entre los diferentes usuarios del dispositivo. Las aplicaciones maliciosas pueden utilizar este permiso para infringir la protección entre usuarios."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"Licencia completa para interactuar con los usuarios"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Permite todas las interacciones posibles con los usuarios."</string>
- <string name="permlab_manageUsers" msgid="1676150911672282428">"Administrar usuarios"</string>
- <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar a los usuarios del dispositivo, incluidas la creación y la eliminación de consultas."</string>
+ <string name="permlab_manageUsers" msgid="1676150911672282428">"administrar usuarios"</string>
+ <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar los usuarios del dispositivo, lo que incluye buscarlos, crearlos y eliminarlos."</string>
<string name="permlab_getDetailedTasks" msgid="6229468674753529501">"recuperar información sobre las aplicaciones en ejecución"</string>
<string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Permite que la aplicación recupere información detallada sobre tareas en ejecución y recientemente ejecutadas. Las aplicaciones malintencionadas pueden hallar información privada sobre otras aplicaciones."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reorganizar aplicaciones en ejecución"</string>
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Admite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento de medios interno."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acceder almacenamiento externo"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que la aplicación acceda al almacenamiento externo de todos los usuarios."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Acceder al sistema de archivos caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar o recibir llamadas por Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exige que se encripten los datos de la aplicación almacenados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desactivar cámaras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Evita el uso de todas las cámaras del dispositivo."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Móvil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Estableciendo conexión con la VPN siempre activada..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Se estableció conexión con la VPN siempre activada."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Se produjo un error al establecer conexión con la VPN siempre activada."</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Elegir archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"No se seleccionó un archivo."</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Listo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Pantalla integrada"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición N.°<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Olvidaste el patrón?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingresa el PIN de la tarjeta SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingresa el PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ingresa tu contraseña."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Código PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nuevo código PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando tarjeta SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorrecto"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Escribe un PIN que tenga de 4 a 8 números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Ingresa un código PUK de ocho números o más."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Escribe el código PUK y un nuevo código PIN."</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"El código PUK que escribiste es incorrecto."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiados intentos incorrectos para ingresar el patrón"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, inicia sesión en tu cuenta de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nombre de usuario (correo electrónico)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contraseña"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Acceder"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nombre de usuario o contraseña incorrectos"</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"¿Olvidaste tu nombre de usuario o contraseña?"\n"Accede a "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Comprobando..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Intentaste desbloquear la tableta <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica de la tableta y se pierdan todos los datos de usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo lograste. Puedes intentarlo <xliff:g id="NUMBER_1">%d</xliff:g> veces más antes de que se restablezcan los valores predeterminados de fábrica del dispositivo y se pierdan todos los datos de usuario."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Intentaste desbloquear la tableta <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica de la tableta."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Intentaste desbloquear el dispositivo <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo lograste. Se restablecerán los valores predeterminados de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu tableta mediante el uso de una cuenta de correo electrónico."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo electrónico."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5e63311..f50c797 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar o eliminar el contenido del almacenamiento de medios interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento multimedia interno."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acceder almacenamiento externo"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que la aplicación acceda al almacenamiento externo de todos los usuarios."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acceder al sistema de archivos almacenado en caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar/recibir llamadas por Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exige que se encripten los datos de la aplicación almacenados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Inhabilitar cámaras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar el uso de las cámaras del dispositivo"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Móvil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Conectando VPN siempre activada…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN siempre activada conectada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de VPN siempre activada"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Seleccionar archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Archivo no seleccionado"</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fin"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Pantalla integrada"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Pantalla integrada"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposición #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Has olvidado el patrón?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorrecto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Inténtalo de nuevo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dibuja tu patrón."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduce el PIN de la tarjeta SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduce el PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Escribe tu contraseña."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Código PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nuevo código PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando tarjeta SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorrecto"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduce un código PIN con una longitud comprendida entre cuatro y ocho dígitos."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Escribe un código PUK de ocho caracteres o más."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Introduce el código PUK y un nuevo código PIN."</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"El código PUK que has introducido no es correcto."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiados intentos incorrectos de crear el patrón"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear el teléfono, inicia sesión con tu cuenta de Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nombre de usuario (correo electrónico)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Contraseña"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sesión"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"El nombre de usuario o la contraseña no son válidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Si has olvidado tu nombre de usuario o tu contraseña,"\n"accede a la página "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Comprobando..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar tu patrón de desbloqueo. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Has intentado desbloquear el tablet <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER_0">%d</xliff:g> veces, pero no lo has conseguido. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, se restablecerán los datos de fábrica y se perderán todos los datos del usuario."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Has intentado desbloquear el tablet <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Has intentado desbloquear el teléfono <xliff:g id="NUMBER">%d</xliff:g> veces, pero no lo has conseguido. Se restablecerán los datos de fábrica del dispositivo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el tablet."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8280b61..c2693e8 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Võimaldab rakendusel kirjutada SD-kaardile."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sisemälu sisu muutm./kustut."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Võimaldab rakendusel muuta sisemise andmekandja sisu."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"juurdepääs kõikide kasutajate välismäluseadmele"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Võimaldab rakenduse kõikidel kasutajatel hankida juurdepääsu välismäluseadmele."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"juurdepääs vahemälu failisüsteemile"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Võimaldab rakendusel vahemälu failisüsteemi lugeda ja kirjutada."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Interneti-kõnede tegemine/vastuvõtmine"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Nõua salvestatud rakenduse andmete krüpteerimist."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Keela kaamerad"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Vältige seadme kõigi kaamerate kasutamist."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Kodu"</item>
<item msgid="869923650527136615">"Mobiil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ühendatud seansiga <xliff:g id="SESSION">%s</xliff:g>. Võrgu haldamiseks puudutage."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ühendamine alati sees VPN-iga …"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ühendatud alati sees VPN-iga"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alati sees VPN-i viga"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Ühenduse lähtestamiseks puudutage"</string>
<string name="upload_file" msgid="2897957172366730416">"Valige fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ühtegi faili pole valitud"</string>
<string name="reset" msgid="2448168080964209908">"Lähtesta"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-heli"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Valmis"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Meediaväljund"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Sisseehitatud ekraan"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Sisseehitatud ekraan"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Ülekate nr<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hädaabikõne"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Unustasite mustri"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Vale muster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Vale parool"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Vale PIN-kood"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Proovige uuesti <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Joonistage oma muster"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Sisestage SIM-i PIN-kood"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Sisestage PIN-kood"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Sisestage parool"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-kood"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Uus PIN-kood"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kaardi avamine ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Vale PIN-kood."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Sisestage 4–8-numbriline PIN-kood."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Sisestage 8-numbriline või pikem PUK-kood."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Sisestage PUK-kood ja uus PIN-kood"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Sisestatud PUK-kood pole õige."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Liiga palju mustrikatseid"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Avamiseks logige sisse oma Google\'i kontoga."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Kasutajanimi (e-post)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parool"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logi sisse"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Vale kasutajanimi või parool."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kas unustasite kasutajanime või parooli?"\n"Külastage aadressi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrollimine ..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olete PIN-koodi <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olete parooli <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. "\n\n"Proovige uuesti <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda tahvelarvutit valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse tahvelarvuti tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Olete üritanud <xliff:g id="NUMBER_0">%d</xliff:g> korda telefoni valesti avada. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset lähtestatakse telefon tehase vaikeseadetele ja kõik kasutajaandmed lähevad kaotsi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Olete püüdnud tahvelarvutit <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Tahvelarvuti lähtestatakse nüüd tehase vaikeseadetele."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Olete püüdnud telefoni <xliff:g id="NUMBER">%d</xliff:g> korda valesti avada. Telefon lähtestatakse nüüd tehase vaikeseadetele."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Kui olete teinud veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset, palutakse teil tahvelarvuti avada meilikontoga."\n\n" Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Kui olete teinud veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset, palutakse teil telefon avada meilikontoga."\n\n" Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 0dba1bd..da5ff94 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -85,7 +85,7 @@
<string name="serviceClassFAX" msgid="5566624998840486475">"نمابر"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"پیامک"</string>
<string name="serviceClassDataAsync" msgid="4523454783498551468">"غیر همگام"</string>
- <string name="serviceClassDataSync" msgid="7530000519646054776">"همگام سازی"</string>
+ <string name="serviceClassDataSync" msgid="7530000519646054776">"همگامسازی"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"بسته"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
<string name="roamingText0" msgid="7170335472198694945">"نشانگر رومینگ روشن"</string>
@@ -126,8 +126,8 @@
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"فایل درخواستی پیدا نشد."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"درخواستهای زیادی در حال پردازش است. بعداً دوباره امتحان کنید."</string>
<string name="notification_title" msgid="8967710025036163822">"خطای ورود به سیستم برای <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
- <string name="contentServiceSync" msgid="8353523060269335667">"همگام سازی"</string>
- <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگام سازی"</string>
+ <string name="contentServiceSync" msgid="8353523060269335667">"همگامسازی"</string>
+ <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگامسازی"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"تعداد موارد حذف شده <xliff:g id="CONTENT_TYPE">%s</xliff:g> بسیار زیاد است."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"حافظه رایانهٔ لوحی پر است! برخی از فایلها را حذف کنید تا فضا آزاد شود."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"حافظه تلفن پر است. بعضی از فایلها را حذف کنید تا فضا آزاد شود."</string>
@@ -464,8 +464,8 @@
<string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"به برنامه اجازه میدهد رایانهٔ لوحی را روشن یا خاموش کند."</string>
<string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"به برنامه اجازه میدهد گوشی را روشن یا خاموش کند."</string>
<string name="permlab_factoryTest" msgid="3715225492696416187">"اجرا در حالت تست کارخانه"</string>
- <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
- <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"تنظیم تصویر زمینه"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"به برنامه اجازه میدهد تا تصویر زمینه سیستم را تنظیم کند."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"تنظیم اندازه تصویر زمینه"</string>
@@ -478,7 +478,7 @@
<string name="permlab_setTimeZone" msgid="2945079801013077340">"تنظیم منطقهٔ زمانی"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"به برنامه اجازه میدهد تا منطقهٔ زمانی رایانهٔ لوحی را تغییر دهد."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"به برنامه اجازه میدهد تا منطقهٔ زمانی تلفن را تغییر دهد."</string>
- <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد به عنوان AccountManagerService"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد بهعنوان AccountManagerService"</string>
<string name="permdesc_accountManagerService" msgid="1948455552333615954">"به برنامه اجازه میدهد با AccountAuthenticators تماس برقرار کند."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"یافتن حسابها در دستگاه"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"به برنامه اجازه میدهد به لیست حسابهای شناخته شده توسط رایانهٔ لوحی دسترسی پیدا کند. این ممکن است حسابهای ایجاد شده توسط برنامههایی را که نصب کردهاید، شامل شود."</string>
@@ -502,7 +502,7 @@
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"تغییر تنظیمات میزان استفاده داده در پسزمینه"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"به برنامه اجازه میدهد تا تنظیم کاربرد دادههای پسزمینه را تغییر دهد."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"مشاهدهٔ اتصالات Wi-Fi"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، به عنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، بهعنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"اتصال به Wi-Fi و قطع اتصال از آن"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"به برنامه اجازه میدهد تا به نقاط دسترسی Wi-Fi وصل شود و ارتباط خود را با آنها قطع کند و تغییراتی را در پیکربندی دستگاه برای شبکههای Wi-Fi ایجاد کند."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"دریافت چندگانه Wi-Fi را مجاز میکند"</string>
@@ -522,12 +522,12 @@
<string name="permlab_nfc" msgid="4423351274757876953">"کنترل ارتباط راه نزدیک"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. به عنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
- <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام سازی"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. به عنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
+ <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگامسازی"</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. بهعنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگامسازی بین فعال و غیرفعال"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. به عنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
- <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگام سازی"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. بهعنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
+ <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگامسازی"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"به یک برنامه اجازه میدهد وضعیت همگامسازی یک حساب را بخواند، از جمله سابقه رویدادهای همگامسازی و میزان دادههای همگامسازی شده."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"خواندن فیدهای مشترک"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"به برنامه اجازه میدهد تا جزئیات مربوط به فیدهای همگام شده کنونی را دریافت کند."</string>
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"به برنامه اجازه میدهد تا در کارت SD بنویسد."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تغییر/حذف محتواهای حافظه رسانه داخلی"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"به برنامه اجازه میدهد تا محتویات حافظه رسانه داخلی را تغییر دهد."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"دسترسی به دستگاه ذخیره خارجی تمام کاربران"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"به برنامه اجازه میدهد به دستگاه ذخیره خارجی برای همه کاربران دسترسی داشته باشد."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"دسترسی به سیستم فایل حافظهٔ پنهان"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"به برنامه اجازه میدهد تا سیستم فایل حافظهٔ پنهان را بخواند و بنویسد."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"علامتگذاری/دریافت تماسهای اینترنتی"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"باید اطلاعات ذخیره شده برنامه رمزگذاری شود."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"غیر فعال کردن دوربین ها"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"از استفاده از تمام دوربینهای دستگاه جلوگیری کنید."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"خانه"</item>
<item msgid="869923650527136615">"تلفن همراه"</item>
@@ -1089,9 +1095,9 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"در صورت فعال کردن حافظهٔ USB، برخی از برنامههایی که از آنها استفاده میکنید متوقف میشوند و تا زمانی که حافظهٔ USB را غیرفعال نکنید امکان استفاده از آنها وجود نخواهد داشت."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"راهاندازی USB ناموفق بود."</string>
<string name="dlg_ok" msgid="7376953167039865701">"تأیید"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده به عنوان دستگاه رسانهای"</string>
- <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده به عنوان دوربین"</string>
- <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده به عنوان نصب کننده"</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده بهعنوان دستگاه رسانهای"</string>
+ <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده بهعنوان دوربین"</string>
+ <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده بهعنوان نصب کننده"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"به یک وسیله جانبی USB وصل شده است"</string>
<string name="usb_notification_message" msgid="2290859399983720271">"برای سایر گزینههای USB لمس کنید."</string>
<string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"حافظهٔ USB فرمت شود؟"</string>
@@ -1159,7 +1165,7 @@
<string name="permission_request_notification_title" msgid="6486759795926237907">"مجوز درخواست شد"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"مجوز"\n"برای حساب <xliff:g id="ACCOUNT">%s</xliff:g> درخواست شد."</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"روش ورودی"</string>
- <string name="sync_binding_label" msgid="3687969138375092423">"همگام سازی"</string>
+ <string name="sync_binding_label" msgid="3687969138375092423">"همگامسازی"</string>
<string name="accessibility_binding_label" msgid="4148120742096474641">"قابلیت دسترسی"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"تصویر زمینه"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر تصویر زمینه"</string>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"به <xliff:g id="SESSION">%s</xliff:g> وصل شد. برای مدیریت شبکه لمس کنید."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"در حال اتصال VPN همیشه فعال…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN همیشه فعال متصل شد"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطای VPN همیشه فعال"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"برای بازنشانی اتصال لمس کنید"</string>
<string name="upload_file" msgid="2897957172366730416">"انتخاب فایل"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"هیچ فایلی انتخاب نشد"</string>
<string name="reset" msgid="2448168080964209908">"بازنشانی"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"بلوتوثهای صوتی"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"انجام شد"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"خروجی رسانه"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"صفحه نمایش از خود"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"صفحه نمایش از خود"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"همپوشانی #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"تماس اضطراری"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"الگو را فراموش کردهاید"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"الگوی اشتباه"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"گذرواژه اشتباه"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"پین اشتباه"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"پس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"رسم الگوی خود"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"پین سیم کارت را وارد کنید"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"پین را وارد کنید"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"گذرواژه را وارد کنید"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"کد PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"پین کد جدید"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"بازگشایی قفل سیم کارت..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"پین کد اشتباه است."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"یک پین ۴ تا ۸ رقمی را تایپ کنید."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"یک PUK با ۸ رقم یا بیشتر تایپ کنید."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK و پین کد جدید را تایپ کنید"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK که نوشتهاید صحیح نیست."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"تلاشهای زیادی برای کشیدن الگو صورت گرفته است"</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="8849589033659332457">"در حال بررسی..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"پین خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"گذرواژه خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدید. "\n\n"لطفاً پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"شما به اشتباه <xliff:g id="NUMBER_0">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f6ab9f8..770297c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Antaa sovelluksen kirjoittaa SD-kortille."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"muokkaa/poista sisäisen säilytystilan sisältöä"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Antaa sovelluksen muokata sisäisen tallennustilan sisältöä."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"käyttää kaikkien käyttäjien ulk. tallennustilaa"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Sallii sovelluksen käyttää ulkoista tallennustilaa kaikille käyttäjille."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"käytä välimuistin tiedostojärjestelmää"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Antaa sovelluksen lukea välimuistin tiedostojärjestelmää ja kirjoittaa siihen."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"soita/vastaanota internetpuheluita"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Pakota tallennettujen sovellustietojen salaus."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Poista kamerat käytöstä"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Estä laitteen kaikkien kameroiden käyttö."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Puhelinnumero (koti)"</item>
<item msgid="869923650527136615">"Mobiili"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Yhdistetty: <xliff:g id="SESSION">%s</xliff:g>. Hallinnoi verkkoa koskettamalla."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Yhdistetään aina käytössä olevaan VPN-verkkoon..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Yhdistetty aina käytössä olevaan VPN-verkkoon"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Aina käytössä oleva VPN: virhe"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Nollaa yhteys koskettamalla"</string>
<string name="upload_file" msgid="2897957172366730416">"Valitse tiedosto"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ei valittua tiedostoa"</string>
<string name="reset" msgid="2448168080964209908">"Palauta"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ääni"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Valmis"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Median äänentoisto"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Sisäänrakennettu näyttö"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Yhdysrakenteinen näyttö"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Peittokuva # <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hätäpuhelu"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Unohtunut kuvio"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Väärä kuvio"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Väärä salasana"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Väärä PIN-koodi"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Yritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Piirrä kuvio"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Anna SIM-kortin PIN-koodi"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Anna PIN-koodi"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Anna salasana"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-koodi"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Uusi PIN-koodi"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM-kortin lukitusta poistetaan…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Virheellinen PIN-koodi."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Anna 4–8-numeroinen PIN-koodi."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Kirjoita vähintään 8 numeron pituinen PUK-koodi."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Anna PUK-koodi ja uusi PIN-koodi"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Antamasi PUK-koodi on virheellinen."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Liikaa kuvionpiirtoyrityksiä"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Poista lukitus kirjautumalla sisään Google-tililläsi."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Käyttäjänimi (sähköposti)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Salasana"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Kirjaudu sisään"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Virheellinen käyttäjänimi tai salasana."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Unohditko käyttäjänimesi tai salasanasi?"\n"Käy osoitteessa "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Tarkistetaan..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, tablet-laitteeseen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos teet vielä <xliff:g id="NUMBER_1">%d</xliff:g> epäonnistunutta yritystä, puhelimeen palautetaan tehdasasetukset ja kaikki käyttäjätiedot häviävät."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet-laitteen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Laitteeseen palautetaan nyt tehdasasetukset."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Puhelimen lukituksen poisto epäonnistui <xliff:g id="NUMBER">%d</xliff:g> kertaa. Puhelimeen palautetaan nyt tehdasasetukset."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan tablet-laitteesi lukitus sähköpostitilin avulla."\n\n" Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla."\n\n" Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d4d938b..c23a8e3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Modifier/Supprimer contenu mémoire interne"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet à l\'application de modifier le contenu de la mémoire de stockage multimédia interne."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Accès stock. ext. tous utilis."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permet à l\'application d\'accéder à la mémoire de stockage externe pour tous les utilisateurs."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accéder au système de fichiers en cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet à l\'application d\'obtenir des droits en lecture/écriture concernant le système de fichiers du cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effectuer/recevoir des appels Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exiger le chiffrement des données d\'application stockées"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Désactiver les appareils photo"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Empêcher l\'utilisation de tous les appareils photos"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domicile"</item>
<item msgid="869923650527136615">"Mobile"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Connecté à <xliff:g id="SESSION">%s</xliff:g>. Appuyez ici pour gérer le réseau."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN permanent en cours de connexion…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN permanent connecté"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erreur du VPN permanent"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Appuyez sur l\'écran pour réinitialiser la connexion."</string>
<string name="upload_file" msgid="2897957172366730416">"Sélectionner un fichier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Aucun fichier sélectionné"</string>
<string name="reset" msgid="2448168080964209908">"Réinitialiser"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"OK"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Sortie multimédia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Écran intégré"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Écran intégré"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Superposition n° <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Appel d\'urgence"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"J\'ai oublié le schéma"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Schéma incorrect."</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Mot de passe incorrect."</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Code PIN incorrect."</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dessinez votre schéma."</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Saisissez le code PIN de la carte SIM."</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Saisissez le code PIN."</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Saisissez votre mot de passe."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Clé PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nouveau code PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Déblocage de la carte SIM en cours…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Le code PIN est erroné."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Saisissez un code PIN comprenant entre quatre et huit chiffres"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Saisissez une clé PUK comportant au moins huit chiffres"</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Saisissez la clé PUK et le nouveau code PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"La clé PUK saisie est incorrecte."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Trop de tentatives."</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Pour déverrouiller le téléphone, veuillez vous connecter avec votre compte Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nom d\'utilisateur (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Mot de passe"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Connexion"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'utilisateur ou mot de passe non valide."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vous avez oublié votre nom d\'utilisateur ou votre mot de passe ?"\n"Rendez-vous sur la page "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Vérification en cours…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises."\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, sa configuration d\'usine sera rétablie, et toutes les données utilisateur seront perdues."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Vous avez tenté de déverrouiller la tablette de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Vous avez tenté de déverrouiller le téléphone de façon incorrecte à <xliff:g id="NUMBER">%d</xliff:g> reprises. Sa configuration d\'usine va être rétablie."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre tablette à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> échecs supplémentaires, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b491e97..e338c95 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -468,7 +468,7 @@
<string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"फ़ोन हार्डवेयर में पूर्ण पहुंच की अनुमति देते हुए निम्न-स्तर निर्माता परीक्षण के रूप में चलाएं. केवल तभी उपलब्ध जब कोई फ़ोन निर्माता परीक्षण मोड में चल रहा हो."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"वॉलपेपर सेट करें"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"एप्लिकेशन को सिस्टम वॉलपेपर सेट करने देता है."</string>
- <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"अपने वॉलपेपर का आकार समायोजित करें"</string>
+ <string name="permlab_setWallpaperHints" msgid="3278608165977736538">"अपने वॉलपेपर का आकार एडजस्ट करें"</string>
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"एप्लिकेशन को सिस्टम वॉलपेपर आकार संकेत सेट करने देता है."</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"फ़ैक्ट्री डिफ़ॉल्ट पर सिस्टम रीसेट करें"</string>
<string name="permdesc_masterClear" msgid="3665380492633910226">"एप्लिकेशन को सभी डेटा, कॉन्फ़िगरेशन, और इंस्टॉल एप्लिकेशन मिटाकर, सिस्टम को पूरी तरह उसकी फ़ैक्टरी सेटिंग पर रीसेट करने देता है."</string>
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"एप्लिकेशन को SD कार्ड पर लिखने देता है."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"आंतरिक मीडिया संग्रहण सामग्रियों को बदलें/हटाएं"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"एप्लिकेशन को आंतरिक मीडिया संग्रहण की सामग्री को संशोधित करने देता है."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचें"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"एप्लिकेशन को सभी उपयोगकर्ताओं के बाहरी संग्रहण तक पहुंचने दें."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"कैश फ़ाइल सिस्टम में पहंचे"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"एप्लिकेशन को संचय फ़ाइल सिस्टम पढ़ने और लिखने देता है."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"इंटरनेट कॉल करें/प्राप्त करें"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"संग्रहीत एप्लिकेशन डेटा को एन्क्रिप्ट किया जाना आवश्यक है."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"कैमरों को अक्षम करें"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"सभी उपकरण कैमरों का उपयोग रोकें."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"घर"</item>
<item msgid="869923650527136615">"मोबाइल"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> से कनेक्ट किया गया. नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"हमेशा-चालू VPN कनेक्ट हो रहा है…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"हमेशा-चालू VPN कनेक्ट है"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"हमेशा-चालू VPN त्रुटि"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"कनेक्शन रीसेट करने के लिए स्पर्श करें"</string>
<string name="upload_file" msgid="2897957172366730416">"फ़ाइल चुनें"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"कोई फ़ाइल चुनी नहीं गई"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करें"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ऑडियो"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"पूर्ण"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"मीडिया आउटपुट"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"अंतर्निहित स्क्रीन"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"अंतर्निहित स्क्रीन"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"ओवरले #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"आपातकालीन कॉल"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"प्रतिमान भूल गए"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"गलत प्रतिमान"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"गलत पासवर्ड"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"गलत PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"अपना प्रतिमान आरेखित करें"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"सिम PIN डालें"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN डालें"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"पासवर्ड डालें"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK कोड"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"नया PIN कोड"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"सिम कार्ड अनलॉक कर रहा है…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"गलत PIN कोड."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ऐसा PIN लिखें, जो 4 से 8 अंकों का हो."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"ऐसा PUK लिखें जो 8 अंकों या अधिक का हो."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK और नया PIN कोड लिखें"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"आपके द्वारा लिखा गया PUK सही नहीं है."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"बहुत अधिक प्रतिमान प्रयास"</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="8849589033659332457">"जांच कर रहा है..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक प्रतिमान <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आप टेबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, टेबलेट फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, फ़ोन फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index aef3bdf..bf7dc5e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogućuje pisanje na SD karticu."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"izmijeni/izbriši sadržaj pohranjen na internim medijima"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Aplikaciji omogućuje izmjenu sadržaja pohranjenog na internim medijima."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pristup vanjskoj pohrani svima"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Omogućuje aplikaciji pristup vanjskoj pohrani za sve korisnike."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristup sustavu datoteka predmemorije"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Aplikaciji omogućuje čitanje i pisanje u datotečnom sustavu privremene memorije."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"zovi/primaj internetske pozive"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Zahtijevajte da pohranjeni podaci aplikacije budu šifrirani."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Onemogući fotoaparate"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Spriječite upotrebu svih kamera uređaja."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Početna"</item>
<item msgid="869923650527136615">"Mobilni"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Povezan sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite za upravljanje mrežom."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje s uvijek uključenom VPN mrežom…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan s uvijek uključenom VPN mrežom"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Pogreška uvijek uključene VPN mreže"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dodirnite za poništavanje veze"</string>
<string name="upload_file" msgid="2897957172366730416">"Odaberite datoteku"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nema odabranih datoteka"</string>
<string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth zvuk"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medijski izlaz"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ugrađeni zaslon"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ugrađeni zaslon"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Preklapanje br. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Hitan poziv"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zaboravili ste obrazac"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Pogrešan obrazac"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Pogrešna zaporka"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Pogrešan PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Iscrtajte svoj obrazac"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Unesite PIN za SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Unesite zaporku"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK kôd"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Novi PIN kôd"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Otključavanje SIM kartice…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Netočan PIN kôd."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Unesite PIN koji ima od 4 do 8 brojeva."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Upišite PUK koji se sastoji od barem 8 brojeva."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Unesite PUK i novi PIN kôd"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK koji ste unijeli nije točan."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Previše pokušaja iscrtavanja obrasca"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Za otključavanje prijavite se Google računom."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Korisničko ime (e-pošta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Zaporka"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nevažeće korisničko ime ili zaporka."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zaboravili ste korisničko ime ili zaporku?"\n"Posjetite "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Provjeravanje…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER_0">%d</xliff:g> puta. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER_0">%d</xliff:g> puta. On će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Netočno ste pokušali otključati telefon <xliff:g id="NUMBER">%d</xliff:g> puta. Sada će se vratiti na tvorničke postavke."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati tabletno računalo pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 90bd85e..0404e3f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lehetővé teszi az alkalmazás számára, hogy írjon az SD-kártyára."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"belső tár tartalmának módosítása/törlése"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a belső háttértár tartalmát."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"hozzáf. minden felh. külső tár"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Minden felhasználó számára lehetővé teszi, hogy az alkalmazás hozzáférjen külső tárolókhoz."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"hozzáférés a gyorsítótár fájlrendszeréhez"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lehetővé teszi az alkalmazás számára a gyorsítótár-fájlrendszer olvasását és írását."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetes hívások kezdeményezése és fogadása"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Megköveteli a tárolt alkalmazásadatok titkosítását."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Kamerák letiltása"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Az összes eszközkamera használatának megakadályozása."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Otthoni"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Csatlakozva ide: <xliff:g id="SESSION">%s</xliff:g>. Érintse meg a hálózat kezeléséhez."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Csatlakozás a mindig bekapcsolt VPN-hez..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Csatlakozva a mindig bekapcsolt VPN-hez"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Hiba a mindig bekapcsolt VPN-nel"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Érintse meg a kapcsolat alaphelyzetbe állításához."</string>
<string name="upload_file" msgid="2897957172366730416">"Fájl kiválasztása"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nincs fájl kiválasztva"</string>
<string name="reset" msgid="2448168080964209908">"Alaphelyzet"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth hang"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Kész"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Médiakimenet"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Beépített képernyő"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Beépített képernyő"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"<xliff:g id="ID">%1$d</xliff:g>. fedvény"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Segélyhívás"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Elfelejtett minta"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Helytelen minta"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Helytelen jelszó"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Helytelen PIN kód"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Próbálkozzon újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Rajzolja le a mintát"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Adja meg a SIM kártya PIN kódját"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Adja meg a PIN kódot"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Írja be a jelszót"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK kód"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Új PIN kód"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kártya feloldása..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Helytelen PIN kód."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4–8 számjegyű PIN kódot írjon be."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"8 számjegyű vagy hosszabb PUK kódot írjon be."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Írja be a PUK kódot, majd az új PIN kódot"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"A megadott PUK kód helytelen."</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_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>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Elfelejtette a felhasználónevét vagy jelszavát?"\n"Keresse fel a "<b>"google.com/accounts/recovery"</b>" webhelyet."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Ellenőrzés..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg PIN kódját. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a jelszót. "\n\n" Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"A táblagépet <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a táblagép gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"A telefont <xliff:g id="NUMBER_0">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. <xliff:g id="NUMBER_1">%d</xliff:g> további sikertelen próbálkozás után a rendszer visszaállítja a telefon gyári alapértelmezett beállításait, és minden felhasználói adat elvész."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"A táblagépet <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a táblagép gyári alapértelmezett beállításait."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"A telefont <xliff:g id="NUMBER">%d</xliff:g> alkalommal próbálta meg sikertelenül feloldani. A rendszer visszaállítja a telefon gyári alapértelmezett beállításait."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a táblagépét."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 34e6a96..8a2ae29 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubah/hapus konten penyimpanan media internal"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Mengizinkan apl memodifikasi konten penyimpanan media internal."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"akses penyimpanan eksternal"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Izinkan aplikasi mengakses penyimpanan eksternal untuk semua pengguna."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem file cache."</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Mengizinkan apl membaca dan menulis pada sistem file cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"lakukan//terima panggilan internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Mengharuskan data apl yang disimpan untuk dienkripsi."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Nonaktifkan kamera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Mencegah penggunaan semua kamera perangkat."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Rumah"</item>
<item msgid="869923650527136615">"Seluler"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Tersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengelola jaringan."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Menyambungkan VPN selalu aktif..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN selalu aktif tersambung"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kesalahan VPN selalu aktif"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menyetel ulang sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tidak ada file yang dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Setel ulang"</string>
@@ -1198,7 +1208,7 @@
<string name="media_bad_removal" msgid="7960864061016603281">"Kartu SD dihapus sebelum dilepas."</string>
<string name="media_checking" product="nosdcard" msgid="418188720009569693">"Saat ini penyimpanan USB sedang diperiksa."</string>
<string name="media_checking" product="default" msgid="7334762503904827481">"Kartu SD sedang diperiksa."</string>
- <string name="media_removed" msgid="7001526905057952097">"Kartu SD telah dihapus."</string>
+ <string name="media_removed" msgid="7001526905057952097">"Kartu SD telah dikeluarkan."</string>
<string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Saat ini penyimpanan USB sedang digunakan oleh komputer."</string>
<string name="media_shared" product="default" msgid="5706130568133540435">"Kartu SD sedang digunakan oleh komputer."</string>
<string name="media_unknown_state" msgid="729192782197290385">"Media eksternal dalam status tidak diketahui."</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Selesai"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Keluaran media"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Layar Ada Di Dalamnya"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Layar Bawaan"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Hamparan #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Panggilan darurat"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Lupa Pola?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Pola Salah"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Sandi Salah"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Salah"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Coba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Gambar pola Anda"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Masukkan Sandi"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Kode PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Kode PIN baru"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Membuka kunci kartu SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Kode PIN salah."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ketik PIN yang terdiri dari 4 sampai 8 angka."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Ketik PUK yang terdiri dari 8 angka atau lebih."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Ketik kode PUK dan PIN baru"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK yang Anda ketikkan salah."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Terlalu banyak upaya pola"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Untuk membuka kunci, masuk dengan akun Google Anda."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nama pengguna (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Sandi"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Masuk"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau sandi tidak valid."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau sandi Anda?"\n"Kunjungi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Memeriksa…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik PIN. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik sandi. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan disetel ulang ke setelan default pabrik."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan disetel ulang ke setelan default pabrik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a693dc8..3b2f460 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Consente all\'applicazione di scrivere sulla scheda SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modifica/eliminaz. contenuti archivio media int."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Consente all\'applicazione di modificare i contenuti dell\'archivio media interno."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"accesso memoria esterna utenti"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Consente all\'applicazione di accedere alla memoria esterna di tutti gli utenti."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesso al filesystem nella cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Consente all\'applicazione di leggere e scrivere il filesystem nella cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effettuazione/ricezione chiamate Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Richiede la crittografia dei dati applicazione memorizzati."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Disattiva fotocamere"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Impedisci l\'utilizzo di tutte le fotocamere del dispositivo."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Cellulare"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN attivata da <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tocca per gestire la rete."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Collegata a <xliff:g id="SESSION">%s</xliff:g>. Tocca per gestire la rete."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Connessione a VPN sempre attiva…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre attiva connessa"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Errore VPN sempre attiva"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Tocca per ripristinare la connessione"</string>
<string name="upload_file" msgid="2897957172366730416">"Scegli file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nessun file è stato scelto"</string>
<string name="reset" msgid="2448168080964209908">"Reimposta"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fine"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Uscita media"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Schermo incorporato"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Schermo incorporato"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay n. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chiamata di emergenza"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Sequenza dimenticata"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequenza sbagliata"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Password sbagliata"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN errato"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Inserisci la sequenza"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Inserisci il PIN della SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Inserisci PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Inserisci la password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Codice PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nuovo codice PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Sblocco scheda SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Codice PIN errato."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Il PIN deve essere di 4-8 numeri."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Digita un PUK formato da almeno 8 numeri."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Inserisci il PUK e il nuovo codice PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Il PUK digitato è errato."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Troppi tentativi di inserimento della sequenza"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Per sbloccare, accedi con il tuo account Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome utente (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Accedi"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome utente o password non validi."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Hai dimenticato il nome utente o la password?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Verifica…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Hai digitato la tua password <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del tablet. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il tablet verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di sblocco del telefono. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, il telefono verrà sottoposto a un ripristino dei dati di fabbrica e tutti i dati utente andranno persi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del tablet. Il tablet verrà sottoposto a un ripristino dei dati di fabbrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> tentativi errati di sblocco del telefono. Il telefono verrà sottoposto a un ripristino dei dati di fabbrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il tablet con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a318907..97c3a99 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"מאפשר ליישום לכתוב לכרטיס ה-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"שנה/מחק תוכן של אחסון מדיה פנימי"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"מאפשר ליישום לשנות את התוכן של אמצעי האחסון הפנימי למדיה."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"גישה לאחסון חיצוני של כל המשתמשים"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"מאפשר ליישום לגשת לאחסון חיצוני עבור כל המשתמשים."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"גישה למערכת הקבצים בקובץ השמור"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"מאפשר ליישום לקרוא ולכתוב במערכת הקבצים של הקבצים השמורים."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"בצע/קבל שיחות אינטרנט"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"דרוש שנתוני יישומים מאוחסנים יהיו מוצפנים."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"השבת מצלמות"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"מנע שימוש בכל המצלמות שבמכשיר."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"בית"</item>
<item msgid="869923650527136615">"נייד"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN מופעל על ידי <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"גע כדי לנהל את הרשת."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"מחובר אל <xliff:g id="SESSION">%s</xliff:g>. גע כדי לנהל את הרשת."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"ה-VPN שמופעל תמיד, מתחבר..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"ה-VPN שפועל תמיד, מחובר"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"שגיאת VPN שמופעל תמיד"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"גע כדי לאפס את החיבור"</string>
<string name="upload_file" msgid="2897957172366730416">"בחר קובץ"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"לא נבחר קובץ"</string>
<string name="reset" msgid="2448168080964209908">"איפוס"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"אודיו Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"סיום"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"פלט מדיה"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"מסך מובנה"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"מסך מובנה"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"שכבת על #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"שיחת חירום"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"שכחת את הקו"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"קו ביטול נעילה שגוי"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"סיסמה שגויה"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"מספר PIN שגוי"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"נסה שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"שרטט את קו ביטול הנעילה"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"הזן מספר PIN ל-SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"הזן מספר PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"הזן את הסיסמה"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"קוד PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"קוד PIN חדש"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"מבטל נעילה של כרטיס SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"קוד PIN שגוי."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"הקלד מספר PIN שאורכו 4 עד 8 ספרות."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"הקלד PUK באורך 8 ספרות או יותר."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"הקלד את קוד ה-PUK וקוד ה-PIN החדש"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"ה-PUK שהקלדת שגוי."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ניסיונות רבים מדי לשרטוט קו ביטול נעילה."</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="8849589033659332457">"בודק…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"הקלדת מספר PIN שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטאבלט יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"ביצעת <xliff:g id="NUMBER_0">%d</xliff:g> ניסיונות שגויים לביטול נעילת הטלפון. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, הטלפון יעבור איפוס לברירת המחדל של היצרן וכל נתוני המשתמש יאבדו."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index b7a9c3b..504b57c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SDカードへの書き込みをアプリに許可します。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"内部メディアストレージの内容の変更/削除"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"内部メディアストレージの内容を変更することをアプリに許可します。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"キャッシュファイルシステムにアクセス"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"キャッシュファイルシステムの読み書きをアプリに許可します。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"インターネット通話の発着信"</string>
@@ -577,6 +581,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"保存したアプリデータが暗号化されるようにします。"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"カメラを無効にする"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"すべての端末カメラを使用できないようにします。"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"自宅"</item>
<item msgid="869923650527136615">"携帯"</item>
@@ -1167,6 +1175,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPNが<xliff:g id="APP">%s</xliff:g>により有効化されました"</string>
<string name="vpn_text" msgid="3011306607126450322">"タップしてネットワークを管理します。"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g>に接続しました。ネットワークを管理するにはタップしてください。"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPNに常時接続しています…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPNに常時接続しました"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"常時接続VPNのエラー"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"接続をリセットするにはタップします"</string>
<string name="upload_file" msgid="2897957172366730416">"ファイルを選択"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"ファイルが選択されていません"</string>
<string name="reset" msgid="2448168080964209908">"リセット"</string>
@@ -1311,5 +1323,84 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth音声"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完了"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"メディア出力"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"内蔵画面"</string>
+ <!-- no translation found for display_manager_built_in_display_name (2583134294292563941) -->
+ <skip />
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <!-- no translation found for display_manager_overlay_display_name (5142365982271620716) -->
+ <skip />
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <!-- no translation found for kg_emergency_call_label (684946192523830531) -->
+ <skip />
+ <!-- no translation found for kg_forgot_pattern_button_text (8852021467868220608) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pattern (1850806070801358830) -->
+ <skip />
+ <!-- no translation found for kg_wrong_password (2333281762128113157) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pin (1131306510833563801) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_attempts_countdown (6358110221603297548) -->
+ <skip />
+ <!-- no translation found for kg_pattern_instructions (398978611683075868) -->
+ <skip />
+ <!-- no translation found for kg_sim_pin_instructions (2319508550934557331) -->
+ <skip />
+ <!-- no translation found for kg_pin_instructions (2377242233495111557) -->
+ <skip />
+ <!-- no translation found for kg_password_instructions (5753646556186936819) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_puk_hint (5183097160254244459) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_pin_hint (597821135578014901) -->
+ <skip />
+ <!-- no translation found for kg_sim_unlock_progress_dialog_message (8950398016976865762) -->
+ <skip />
+ <!-- no translation found for kg_password_wrong_pin_code (1139324887413846912) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_pin_hint (8795159358110620001) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_puk_hint (5216603185442368307) -->
+ <skip />
+ <!-- no translation found for kg_sim_puk_recovery_hint (5577753137718442566) -->
+ <skip />
+ <!-- no translation found for kg_invalid_puk (5809955359950817326) -->
+ <skip />
+ <!-- no translation found for kg_login_too_many_attempts (6486842094005698475) -->
+ <skip />
+ <!-- no translation found for kg_login_instructions (1100551261265506448) -->
+ <skip />
+ <!-- no translation found for kg_login_username_hint (5718534272070920364) -->
+ <skip />
+ <!-- no translation found for kg_login_password_hint (9057289103827298549) -->
+ <skip />
+ <!-- no translation found for kg_login_submit_button (5355904582674054702) -->
+ <skip />
+ <!-- no translation found for kg_login_invalid_input (5754664119319872197) -->
+ <skip />
+ <!-- no translation found for kg_login_account_recovery_hint (5690709132841752974) -->
+ <skip />
+ <!-- no translation found for kg_login_checking_password (8849589033659332457) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pin_attempts_dialog_message (8276745642049502550) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_password_attempts_dialog_message (7813713389422226531) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pattern_attempts_dialog_message (74089475965050805) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (1575557200627128949) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (4051015943038199910) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (2072996269148483637) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (4817627474419471518) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (3253575572118914370) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (1437638152015574839) -->
+ <skip />
+ <!-- no translation found for kg_temp_back_string (5812983904056640466) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0193ca2..dfceee6 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"앱이 SD 카드에 쓸 수 있도록 허용합니다."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"내부 미디어 저장소 콘텐츠 수정/삭제"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"앱이 내부 미디어 저장소의 콘텐츠를 수정할 수 있도록 허용합니다."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"모든 사용자의 외부 저장소에 액세스"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"앱이 모든 사용자의 외부 저장소에 액세스하도록 허용합니다."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"캐시 파일시스템 액세스"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"앱이 캐시 파일 시스템을 읽고 쓸 수 있도록 허용합니다."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"인터넷 전화 걸기/받기"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"저장한 애플리케이션 데이터를 암호화해야 합니다."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"카메라 사용 안함"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"모든 기기 카메라의 사용 차단"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"집"</item>
<item msgid="869923650527136615">"모바일"</item>
@@ -794,7 +800,7 @@
<string name="autofill_department" msgid="5343279462564453309">"지역"</string>
<string name="autofill_prefecture" msgid="2028499485065800419">"현"</string>
<string name="autofill_parish" msgid="8202206105468820057">"군"</string>
- <string name="autofill_area" msgid="3547409050889952423">"구역"</string>
+ <string name="autofill_area" msgid="3547409050889952423">"주소"</string>
<string name="autofill_emirate" msgid="2893880978835698818">"에미리트"</string>
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"웹 북마크 및 기록 읽기"</string>
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"앱이 브라우저가 방문한 모든 URL의 기록과 모든 브라우저 북마크를 읽을 수 있도록 허용합니다. 참고: 이 권한은 타사 브라우저 또는 브라우저 기능을 가진 기타 애플리케이션에 적용되지 않습니다."</string>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g>에 연결되어 있습니다. 네트워크를 관리하려면 터치하세요."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"연결 유지 VPN에 연결하는 중…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"연결 유지 VPN에 연결됨"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"연결 유지 VPN 오류"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"터치하여 연결 재설정"</string>
<string name="upload_file" msgid="2897957172366730416">"파일 선택"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"파일을 선택하지 않았습니다."</string>
<string name="reset" msgid="2448168080964209908">"초기화"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"블루투스 오디오"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"완료"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"미디어 출력"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"기본으로 제공되는 화면"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"기본으로 제공되는 화면"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"<xliff:g id="ID">%1$d</xliff:g>번째 오버레이"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"긴급 통화"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"패턴을 잊음"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"잘못된 패턴"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"잘못된 비밀번호"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"잘못된 PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"패턴 그리기"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN 입력"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN 입력"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"비밀번호 입력"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK 코드"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"새 PIN 코드"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM 카드 잠금해제 중..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 코드가 잘못되었습니다."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4~8자리 숫자로 된 PIN을 입력하세요."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"8자리 이상의 숫자 PUK를 입력합니다."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK 및 새 PIN 코드 입력"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"입력한 PUK가 올바르지 않습니다."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"패턴 시도 횟수가 너무 많음"</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="8849589033659332457">"확인 중…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"태블릿을 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 태블릿이 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"휴대전화를 잠금해제하려는 시도가 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못되었습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 휴대전화가 초기화되고 사용자 데이터가 모두 사라집니다."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b778d03..2c5cab5 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leidžiama programai rašyti į SD kortelę."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"keisti / ištr. vid. med. atm. tur."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Leidžiama programai keisti vidinės medijos saugyklos turinį."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pasiekti visų naud. išor. atm."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Leidžiama programai pasiekti visų naudotojų išorinę atmintinę."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pasiekti talpyklos failų sistemą"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Leidžiama programai skaityti talpyklos failų sistemą ir į ją rašyti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"skambinti / priimti skambučius internetu"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Reikalauti, kad saugomos programos duomenys būtų šifruoti."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Neleisti fotoaparatų"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Neleisti naudoti visų įrenginio fotoaparatų."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Pagrindinis"</item>
<item msgid="869923650527136615">"Mobilusis"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Prisijungta prie <xliff:g id="SESSION">%s</xliff:g>. Jei norite valdyti tinklą, palieskite."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prisijungiama prie visada įjungto VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Prisijungta prie visada įjungto VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Visada įjungto VPN klaida"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Palieskite, kad iš naujo nustatytumėte ryšį"</string>
<string name="upload_file" msgid="2897957172366730416">"Pasirinkti failą"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nepasirinktas joks failas"</string>
<string name="reset" msgid="2448168080964209908">"Atstatyti"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"„Bluetooth“ garsas"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Atlikta"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medijos išvestis"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Integruotas ekranas"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Integruotas ekranas"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Perdanga nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Skambutis pagalbos numeriu"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Pamiršau atrakinimo piešinį"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Netinkamas atrakinimo piešinys"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Netinkamas slaptažodis"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Netinkamas PIN kodas"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Bandyti dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nupieškite atrakinimo piešinį"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Įveskite SIM PIN kodą"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Įveskite PIN kodą"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Įveskite slaptažodį"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK kodas"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Naujas PIN kodas"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Atrakinama SIM kortelė…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Netinkamas PIN kodas."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Įveskite PIN kodą, sudarytą iš 4–8 skaičių."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Įveskite 8 skaitmenų ar ilgesnį PUK kodą."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Įveskite PUK ir naują PIN kodus"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Įvestas PUK kodas netinkamas."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Per daug atrakinimo piešinių bandymų"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Jei norite atrakinti, prisijunkite naudodami „Google“ paskyrą."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Naudotojo vardas (el. paštas)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Slaptažodis"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prisijungti"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Netinkamas naudotojo vardas ar slaptažodis."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Pamiršote naudotojo vardą ar slaptažodį?"\n"Apsilankykite šiuo adresu: "<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Tikrinama…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodą netinkamai įvedėte <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"<xliff:g id="NUMBER_0">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai ir bus prarasti visi naudotojo duomenys."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti planšetinį kompiuterį. Planšetiniame kompiuteryje bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"<xliff:g id="NUMBER">%d</xliff:g> k. bandėte netinkamai atrakinti telefoną. Telefone bus iš naujo nustatyti numatytieji gamyklos nustatymai."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti planšetinį kompiuterį naudodami „Google“ prisijungimo duomenis."\n\n" Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis."\n\n" Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 4b78bd4..5be4d0c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -225,7 +225,7 @@
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"pilna licence ar atļauju darboties visos lietotāju kontos"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Ļauj veikt jebkādas darbības visos lietotāju kontos."</string>
<string name="permlab_manageUsers" msgid="1676150911672282428">"Lietotāju pārvaldība"</string>
- <string name="permdesc_manageUsers" msgid="8409306667645355638">"Ļauj lietotnēm pārvaldīt ierīces lietotājus, tostarp izveidot un dzēst lietotājus vai veidot vaicājumus."</string>
+ <string name="permdesc_manageUsers" msgid="8409306667645355638">"Ļauj lietotnēm pārvaldīt ierīces lietotājus, tostarp izveidot un dzēst lietotājus un veidot vaicājumus."</string>
<string name="permlab_getDetailedTasks" msgid="6229468674753529501">"Informācijas izguve par izmantotajām lietotnēm"</string>
<string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Ļauj lietotnei izgūt informāciju par šobrīd un nesen veiktajiem uzdevumiem. Ļaunprātīgas lietotnes var atklāt privātu informāciju par citām lietotnēm."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"pārkārtot izmantotās lietotnes"</string>
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ļauj lietotnei rakstīt SD kartē."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"pārv./dz.datu n.iekš.atm.sat."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ļauj lietotnei modificēt datu nesēja iekšējās atmiņas saturu."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"piekļ. visu liet. ārējai krāt."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Ļauj lietotnei piekļūt visu lietotāju ārējai krātuvei."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"piekļūt kešatmiņas failu sistēmai"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ļauj lietotnei lasīt un rakstīt kešatmiņas failu sistēmā."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"veikt/saņemt interneta zvanus"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Pieprasa, lai saglabātie lietotnes dati tiktu šifrēti."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Atspējot kameras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Neļauj izmantot nevienu ierīces kameru."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Mājas"</item>
<item msgid="869923650527136615">"Mobilais"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ir izveidots savienojums ar <xliff:g id="SESSION">%s</xliff:g>. Pieskarieties, lai pārvaldītu tīklu."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Notiek savienojuma izveide ar vienmēr ieslēgtu VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Izveidots savienojums ar vienmēr ieslēgtu VPN."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kļūda saistībā ar vienmēr ieslēgtu VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pieskarieties, lai atiestatītu savienojumu."</string>
<string name="upload_file" msgid="2897957172366730416">"Izvēlēties failu"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Neviens fails nav izvēlēts"</string>
<string name="reset" msgid="2448168080964209908">"Atiestatīt"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gatavs"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Multivides izeja"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Iebūvēts ekrāns"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Iebūvēts ekrāns"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Pārklājums Nr. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Ārkārtas izsaukums"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Aizmirsu kombināciju"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nepareiza kombinācija"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nepareiza parole"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nepareizs PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Norādiet savu kombināciju"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ievadiet SIM kartes PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ievadiet PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ievadiet paroli"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK kods"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Jauns PIN kods"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Notiek SIM kartes atbloķēšana..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN kods nav pareizs."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ievadiet PIN, kas sastāv no 4 līdz 8 cipariem."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Ievadiet PUK, kas sastāv no vismaz 8 cipariem."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Ievadiet PUK kodu un jaunu PIN kodu"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Ievadītais PUK nav pareizs."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Pārāk daudz kombinācijas mēģinājumu"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Lai atbloķētu, pierakstieties, izmantojot savu Google kontu."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Lietotājvārds (e-pasts)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parole"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Pierakstīties"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nederīgs lietotājvārds vai parole."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vai aizmirsāt lietotājvārdu vai paroli?"\n"Apmeklējiet vietni "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Notiek pārbaude..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jūs nepareizi ievadījāt PIN <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jūs nepareizi ievadījāt paroli <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi un lietotāja dati tiks zaudēti."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Jūs nepareizi veicāt planšetdatora atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Planšetdatorā tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Jūs nepareizi veicāt tālruņa atbloķēšanu <xliff:g id="NUMBER">%d</xliff:g> reizes. Tālrunī tiks atiestatīti rūpnīcas noklusējuma iestatījumi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem planšetdators būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 782c2a7..5f00397 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Membenarkan apl menulis ke kad SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubh suai/pdm kdg strn mdia dlm"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Membenarkan apl mengubah suai kandungan storan media dalaman."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"mengakses storan luaran untuk semua pengguna"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Membenarkan apl untuk mengakses storan luaran untuk semua pengguna."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem fail cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Membenarkan apl membaca dan menulis cache sistem fail."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"membuat/menerima panggilan Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Memerlukan data apl yang disimpan itu disulitkan."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Lumpuhkan kamera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Menghalang penggunaan semua kamera peranti."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Laman Utama"</item>
<item msgid="869923650527136615">"Mudah alih"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Bersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengurus rangkaian."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sentiasa hidup sedang disambungkan..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sentiasa hidup telah disambungkan"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ralat VPN sentiasa hidup"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menetapkan semula sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tiada fail dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Tetapkan semula"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Selesai"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Output media"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Skrin Terbina Dalam"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Skrin Terbina Dalam"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Tindih #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Panggilan kecemasan"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Lupa Corak"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Corak Salah"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Kata Laluan Salah"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN salah"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Cuba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Lukiskan corak anda"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Masukkan PIN SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Masukkan PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Masukkan Kata Laluan"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Kod PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Kod PIN Baharu"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Membuka kunci kad SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Kod PIN salah."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Taipkan PIN yang mengandungi 4 hingga 8 nombor."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Taipkan PUK yang mempunyai 8 nombor atau lebih panjang."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Taip PUK dan kod PIN baharu"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Kod PUK yang anda taipkan tidak betul."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Terlalu banyak percubaan melukis corak"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Untuk membuka kunci, log masuk dengan akaun Google anda."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nama Pengguna (E-mel)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Kata laluan"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log masuk"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau kata laluan tidak sah."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau kata laluan anda?"\n"Lawati"<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Menyemak…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah menaip PIN anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah menaip kata laluan anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Anda telah mencuba untuk membuka kunci tablet dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, tablet akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Anda telah mencuba untuk membuka kunci telefon dengan salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, telefon akan ditetapkan semula kepada tetapan lalai kilang dan semua data pengguna akan hilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula ke tetapan lalai kilang."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Anda telah tersilap melukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci tablet anda menggunakan log masuk Google anda."\n\n" Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda."\n\n" Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3a4212b..8ac299f 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lar appen skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"endre eller slette innhold på interne medier"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lar appen endre innholdet i det interne lagringsmediet."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"åpne eksternlagring for alle brukere"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillater appen å åpne eksternlagring for alle brukere."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"tilgang til bufrede filer"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lar appen lese og skrive til det bufrede filsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foreta/motta Internett-anrop"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Krev at lagrede appdata krypteres."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Deaktiver kameraer"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Hindre bruk av alle kameraer på enheten."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hjemmenummer"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er aktivert av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Trykk for å administrere nettverket."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Koblet til <xliff:g id="SESSION">%s</xliff:g>. Trykk for å administrere nettverket."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Alltid-på VPN kobler til ..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Alltid-på VPN er tilkoblet"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alltid-på VPN-feil"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Trykk for å tilbakestille tilkoblingen"</string>
<string name="upload_file" msgid="2897957172366730416">"Velg fil"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
<string name="reset" msgid="2448168080964209908">"Tilbakestill"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-lyd"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fullført"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieutgang"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Innebygd skjerm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Innebygd skjerm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlegg #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nødnummer"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Har du glemt mønsteret?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Feil mønster"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Feil passord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Feil PIN-kode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Prøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Tegn mønsteret ditt"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Skriv inn PIN-koden for SIM-kortet"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Skriv inn PIN-koden"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Skriv inn passordet"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-kode"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Ny PIN-kode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Låser opp SIM-kortet ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Feil PIN-kode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Skriv inn en PIN-kode på fire til åtte sifre."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Skriv inn en PUK-kode på åtte sifre eller mer."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Skriv inn PUK-koden og en ny PIN-kode"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK-koden du har skrevet inn er ikke riktig."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"For mange forsøk på tegning av mønster"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Logg deg på med Google-kontoen din for å låse opp."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Brukernavn (e-postadresse)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Passord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logg på"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldig brukernavn eller passord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt brukernavnet eller passordet?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrollerer …"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har oppgitt feil PIN-kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles nettbrettet til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, tilbakestilles telefonen til fabrikkstandard og all data går tapt."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har oppgitt feil opplåsningspassord for nettbrettet <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har oppgitt feil opplåsningspassord for telefonen <xliff:g id="NUMBER">%d</xliff:g> ganger. Telefonen tilbakestilles nå til fabrikkstandard."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp nettbrettet via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 89e52a9..a2b2ada 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"inh. mediaopsl. wijz./verw."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Hiermee kan de app de inhoud van de interne mediaopslag aanpassen."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"toegang tot externe opslag van alle gebruikers"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Hiermee krijgt de app toegang tot externe opslag van alle gebruikers."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"het cachebestandssysteem openen"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Hiermee kan de app het cachebestandssysteem lezen en schrijven."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetoproepen starten/ontvangen"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Vereisen dat opgeslagen appgegevens kunnen worden gecodeerd."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Camera\'s uitschakelen"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Het gebruik van alle apparaatcamera\'s voorkomen."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Thuis"</item>
<item msgid="869923650527136615">"Mobiel"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wordt geactiveerd door <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak aan om het netwerk te beheren."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Verbonden met <xliff:g id="SESSION">%s</xliff:g>. Tik om het netwerk te beheren."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Always-on VPN-verbinding maken…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Always-on VPN-verbinding"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fout met Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Raak dit aan om de verbinding opnieuw in te stellen"</string>
<string name="upload_file" msgid="2897957172366730416">"Bestand kiezen"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Geen bestand geselecteerd"</string>
<string name="reset" msgid="2448168080964209908">"Opnieuw instellen"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gereed"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Media-uitvoer"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ingebouwd scherm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ingebouwd scherm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Noodoproep"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Patroon vergeten"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Onjuist patroon"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Onjuist wachtwoord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Onjuiste pincode"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Teken uw patroon"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Geef de pincode van de simkaart op"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Pincode opgeven"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Wachtwoord invoeren"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-code"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nieuwe pincode"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Simkaart ontgrendelen..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Onjuiste pincode."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Voer een pincode van 4 tot 8 cijfers in."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Typ een PUK-code die 8 cijfers of langer is."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Geef de PUK-code en de nieuwe pincode op"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"De PUK-code die u heeft ingevoerd, is onjuist."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Te veel patroonpogingen"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Als u wilt ontgrendelen, moet u zich aanmelden bij uw Google-account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Gebruikersnaam (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Wachtwoord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Aanmelden"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikersnaam of wachtwoord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u uw gebruikersnaam of wachtwoord vergeten?"\n"Ga naar "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Controleren…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"U heeft uw pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"U heeft uw wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"U heeft <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de tablet en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"U heeft nu <xliff:g id="NUMBER_0">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen worden de fabrieksinstellingen hersteld op de telefoon en gaan alle gebruikersgegevens verloren."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de tablet op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de tablet."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"U heeft <xliff:g id="NUMBER">%d</xliff:g> keer geprobeerd de telefoon op een onjuiste manier te ontgrendelen. De fabrieksinstellingen worden nu hersteld op de telefoon."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw tablet te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw telefoon te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f0dcc2f..66995e9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pozwala aplikacji na zapis na karcie SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modyfikowanie/usuwanie zawartości pamięci wew."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pozwala aplikacji na modyfikowanie zawartości pamięci wewnętrznej."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"dostęp do zewnętrznej pamięci wszystkich użytkowników"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Pozwala aplikacji na dostęp do zewnętrznej pamięci masowej dla wszystkich użytkowników."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostęp do systemu plików pamięci podręcznej"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pozwala aplikacji na odczyt i zapis w systemie plików pamięci podręcznej."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"nawiązywanie/odbieranie połączeń przez internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Wymaganie szyfrowania przechowywanych danych aplikacji"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Wyłącz aparaty"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Zapobieganie używaniu wszystkich aparatów w urządzeniu"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Dom"</item>
<item msgid="869923650527136615">"Komórka"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Obsługa sieci VPN została włączona przez aplikację <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotknij, aby zarządzać siecią."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Nawiązano połączenie z: <xliff:g id="SESSION">%s</xliff:g>. Dotknij, aby zarządzać siecią."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Łączę z zawsze włączoną siecią VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Połączono z zawsze włączoną siecią VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Błąd zawsze włączonej sieci VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Kliknij, aby zresetować połączenie"</string>
<string name="upload_file" msgid="2897957172366730416">"Wybierz plik"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nie wybrano pliku"</string>
<string name="reset" msgid="2448168080964209908">"Resetuj"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Dźwięk Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotowe"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Wyjście multimediów"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Wbudowany ekran"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Wbudowany ekran"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Nakładka nr <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Połączenie alarmowe"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nie pamiętam wzoru"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nieprawidłowy wzór"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nieprawidłowe hasło"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nieprawidłowy PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Narysuj swój wzór"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Podaj PIN karty SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Podaj PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Wpisz hasło"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nowy PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odblokowuję kartę SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nieprawidłowy PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Wpisz PIN o długości od 4 do 8 cyfr."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Wpisz PUK składający się z co najmniej 8 cyfr."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Wpisz PUK i nowy PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Podany PUK jest nieprawidłowy."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Zbyt wiele prób narysowania wzoru"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Aby odblokować, zaloguj się na konto Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nazwa użytkownika (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Hasło"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Zaloguj się"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nieprawidłowa nazwa użytkownika lub hasło."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nie pamiętasz nazwy użytkownika lub hasła?"\n"Wejdź na stronę "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Sprawdzam…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowy PIN. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowe hasło. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> narysowałeś nieprawidłowy wzór odblokowania. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Tablet zostanie teraz zresetowany do ustawień fabrycznych."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowałeś nieprawidłowo odblokować telefon. Telefon zostanie teraz zresetowany do ustawień fabrycznych."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 18c25a3..892c88e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que a aplicação escreva no cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./elim. armaz. interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que a aplicação modifique o conteúdo de armazenamento de suportes internos."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"aceder ao armazenamento externo de todos os utilizadores"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que a aplicação aceda ao armazenamento externo para todos os utilizadores."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"aceder ao sistema de ficheiros da cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite à aplicação ler e escrever no sistema de ficheiros da cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Solicitar encriptação dos dados da aplicação armazenados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmaras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar a utilização de todas as câmaras do aparelho."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Residência"</item>
<item msgid="869923650527136615">"Móvel"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN foi ativada pelo <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerir a rede."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ligado a <xliff:g id="SESSION">%s</xliff:g>. Toque para gerir a rede."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"A ligar VPN sempre ativa..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre ativa ligada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro da VPN sempre ativa"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toque para repor a ligação"</string>
<string name="upload_file" msgid="2897957172366730416">"Escolher ficheiro"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Não foi selecionado nenhum ficheiro"</string>
<string name="reset" msgid="2448168080964209908">"Repor"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Concluído"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de som multimédia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ecrã Integrado"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ecrã Integrado"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Sobreposição #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chamada de emergência"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Esqueceu-se da Sequência"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Sequência Incorreta"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Palavra-passe Incorreta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Incorreto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe a sua sequência"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduzir PIN do cartão SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduzir PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introduzir Palavra-passe"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Código PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Novo código PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"A desbloquear cartão SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduza um PIN entre 4 e 8 números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Introduza um PUK que tenha 8 ou mais algarismos."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Introduzir PUK e o novo código PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"O PUK que introduziu não está correto."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiadas tentativas para desenhar sequência"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, inicie sessão com a sua Conta do Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome de utilizador (mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Palavra-passe"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sessão"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de utilizador ou palavra-passe inválidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu-se do nome de utilizador ou da palavra-passe?"\n"Aceda a "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"A verificar…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escreveu o PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escreveu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tentou desbloquear o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER_0">%d</xliff:g> vezes de forma incorreta. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem êxito, as definições de origem do telemóvel serão repostas e todos os dados do utilizador serão perdidos."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tentou desbloquear o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tentou desbloquear o telemóvel <xliff:g id="NUMBER">%d</xliff:g> vezes de forma incorreta, pelo que será reposta a predefinição de fábrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o tablet através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 794af68..812edff 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o aplicativo grave em seu cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/excluir conteúdos de armazenamento de mídia internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que o aplicativo modifique o conteúdo da mídia de armazenamento interno."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acessar arm. ext. dos usuários"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite que o aplicativo acesse o armazenamento externo para todos os usuários."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acessar o sistema de arquivos de cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que o aplicativo leia e grave o sistema de arquivos cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Exija que os dados armazenados do aplicativo sejam criptografados."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmeras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Impeça o uso de todas as câmeras do dispositivo."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Residencial"</item>
<item msgid="869923650527136615">"Celular"</item>
@@ -708,8 +714,8 @@
<string name="lockscreen_emergency_call" msgid="5347633784401285225">"Chamada de emergência"</string>
<string name="lockscreen_return_to_call" msgid="5244259785500040021">"Retornar à chamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
- <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tentar novamente"</string>
- <string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tentar novamente"</string>
+ <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Tente novamente"</string>
+ <string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tente novamente"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
<string name="lockscreen_plugged_in" msgid="8057762828355572315">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN está ativada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerenciar a rede."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toque para gerenciar a rede."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sempre ativa conectando..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sempre ativa conectada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Erro na VPN sempre ativa"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toque para redefinir a conexão"</string>
<string name="upload_file" msgid="2897957172366730416">"Escolher arquivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nenhum arquivo escolhido"</string>
<string name="reset" msgid="2448168080964209908">"Redefinir"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Áudio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Concluído"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Saída de mídia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Tela integrada"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Tela integrada"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Sobreposição nº <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Chamada de emergência"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Esqueci o padrão"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Padrão incorreto"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Senha incorreta"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN incorreto"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe seu padrão"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do cartão SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Digite o PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Digite a senha"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Código PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Novo código PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Digite um PIN com quatro a oito números."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Digite um PUK com oito números ou mais."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Insira o PUK e o novo código PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"O PUK digitado está incorreto."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Muitas tentativas de padrão"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, faça login usando sua Conta do Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nome de usuário (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Senha"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Fazer login"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de usuário ou senha inválidos."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu seu nome de usuário ou senha?"\n"Acesse "<b>"google.com.br/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Verificando..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o tablet será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas malsucedidas, o telefone será redefinido para o padrão de fábrica e todos os dados do usuário serão perdidos."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Você tentou desbloquear incorretamente o tablet <xliff:g id="NUMBER">%d</xliff:g> vezes. O tablet será redefinido para o padrão de fábrica."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Você tentou desbloquear incorretamente o telefone <xliff:g id="NUMBER">%d</xliff:g> vezes. O telefone será redefinido para o padrão de fábrica."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index a093bfb..bf555e8 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -891,6 +891,10 @@
<skip />
<!-- no translation found for permdesc_mediaStorageWrite (8189160597698529185) -->
<skip />
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access al sistem da datotecas da cache"</string>
<!-- no translation found for permdesc_cache_filesystem (5578967642265550955) -->
<skip />
@@ -947,6 +951,10 @@
<skip />
<!-- no translation found for policydesc_disableCamera (2306349042834754597) -->
<skip />
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Privat"</item>
<item msgid="869923650527136615">"Telefonin"</item>
@@ -1799,6 +1807,14 @@
<skip />
<!-- no translation found for vpn_text_long (6407351006249174473) -->
<skip />
+ <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
+ <skip />
+ <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
+ <skip />
+ <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
+ <skip />
+ <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
+ <skip />
<string name="upload_file" msgid="2897957172366730416">"Tscherner ina datoteca"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nagina datoteca tschernida"</string>
<string name="reset" msgid="2448168080964209908">"Reinizialisar"</string>
@@ -2070,6 +2086,84 @@
<skip />
<!-- no translation found for media_route_button_content_description (5758553567065145276) -->
<skip />
- <!-- no translation found for display_manager_built_in_display (9042666544146043569) -->
+ <!-- no translation found for display_manager_built_in_display_name (2583134294292563941) -->
+ <skip />
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <!-- no translation found for display_manager_overlay_display_name (5142365982271620716) -->
+ <skip />
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <!-- no translation found for kg_emergency_call_label (684946192523830531) -->
+ <skip />
+ <!-- no translation found for kg_forgot_pattern_button_text (8852021467868220608) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pattern (1850806070801358830) -->
+ <skip />
+ <!-- no translation found for kg_wrong_password (2333281762128113157) -->
+ <skip />
+ <!-- no translation found for kg_wrong_pin (1131306510833563801) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_attempts_countdown (6358110221603297548) -->
+ <skip />
+ <!-- no translation found for kg_pattern_instructions (398978611683075868) -->
+ <skip />
+ <!-- no translation found for kg_sim_pin_instructions (2319508550934557331) -->
+ <skip />
+ <!-- no translation found for kg_pin_instructions (2377242233495111557) -->
+ <skip />
+ <!-- no translation found for kg_password_instructions (5753646556186936819) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_puk_hint (5183097160254244459) -->
+ <skip />
+ <!-- no translation found for kg_puk_enter_pin_hint (597821135578014901) -->
+ <skip />
+ <!-- no translation found for kg_sim_unlock_progress_dialog_message (8950398016976865762) -->
+ <skip />
+ <!-- no translation found for kg_password_wrong_pin_code (1139324887413846912) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_pin_hint (8795159358110620001) -->
+ <skip />
+ <!-- no translation found for kg_invalid_sim_puk_hint (5216603185442368307) -->
+ <skip />
+ <!-- no translation found for kg_sim_puk_recovery_hint (5577753137718442566) -->
+ <skip />
+ <!-- no translation found for kg_invalid_puk (5809955359950817326) -->
+ <skip />
+ <!-- no translation found for kg_login_too_many_attempts (6486842094005698475) -->
+ <skip />
+ <!-- no translation found for kg_login_instructions (1100551261265506448) -->
+ <skip />
+ <!-- no translation found for kg_login_username_hint (5718534272070920364) -->
+ <skip />
+ <!-- no translation found for kg_login_password_hint (9057289103827298549) -->
+ <skip />
+ <!-- no translation found for kg_login_submit_button (5355904582674054702) -->
+ <skip />
+ <!-- no translation found for kg_login_invalid_input (5754664119319872197) -->
+ <skip />
+ <!-- no translation found for kg_login_account_recovery_hint (5690709132841752974) -->
+ <skip />
+ <!-- no translation found for kg_login_checking_password (8849589033659332457) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pin_attempts_dialog_message (8276745642049502550) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_password_attempts_dialog_message (7813713389422226531) -->
+ <skip />
+ <!-- no translation found for kg_too_many_failed_pattern_attempts_dialog_message (74089475965050805) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (1575557200627128949) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_wipe (4051015943038199910) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (2072996269148483637) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_now_wiping (4817627474419471518) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (3253575572118914370) -->
+ <skip />
+ <!-- no translation found for kg_failed_attempts_almost_at_login (1437638152015574839) -->
+ <skip />
+ <!-- no translation found for kg_temp_back_string (5812983904056640466) -->
<skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 6f6272d..08dca1a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicaţiei să scrie pe cardul SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./şterg. conţinutul media stocat intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite aplicaţiei să modifice conţinutul stocării media interne."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"acces. stoc. ext. pt. toţi utilizat."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Permite aplicaţiei să acceseze stocarea externă pentru toţi utilizatorii."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesare sistem de fişiere cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite aplicaţiei să scrie şi să citească sistemul de fişiere cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"efectuare/primire apeluri prin internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Necesită ca datele aplicaţiei stocate să fie criptate."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Dezactivaţi camerele foto"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Împiedicaţi utilizarea camerelor foto de pe dispozitiv."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domiciliu"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeţi pentru a gestiona reţeaua."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Atingeţi pentru a gestiona reţeaua."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Se efectuează conectarea la reţeaua VPN activată permanent…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Conectat(ă) la reţeaua VPN activată permanent"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Eroare de reţea VPN activată permanent"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Atingeţi pentru a reseta conexiunea"</string>
<string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fişiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Terminat"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Rezultate media"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Ecran încorporat"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ecran încorporat"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Suprapunerea <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Apel de urgenţă"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Model uitat"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Model greşit"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Parolă greşită"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Cod PIN greşit"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Încercaţi din nou peste <xliff:g id="NUMBER">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenaţi modelul"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduceţi codul PIN al cardului SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Introduceţi codul PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Introduceţi parola"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Codul PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Noul cod PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Se deblochează cardul SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Cod PIN incorect."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Introduceţi un cod PIN alcătuit din 4 până la 8 cifre."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Introduceţi un cod PUK care să aibă 8 cifre sau mai mult."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Introduceţi codul PUK şi noul cod PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Codul PUK introdus nu este corect."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Prea multe încercări de desenare a modelului"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Pentru a debloca, conectaţi-vă cu Contul dvs. Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Nume de utilizator (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Parolă"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Conectaţi-vă"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nume de utilizator sau parolă nevalide."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Aţi uitat numele de utilizator sau parola?"\n"Accesaţi "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Se verifică..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%d</xliff:g> ori."\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Aţi efectuat <xliff:g id="NUMBER_0">%d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Aţi efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi tableta cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a9c0701..eae0ecb 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Приложение сможет записывать данные на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"изм./удал. данных мультимедиа"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Приложение сможет изменять контент внутреннего хранилища мультимедиа."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"разрешить доступ к внешним накопителям из всех аккаунтов"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Приложение сможет обращаться к внешним накопителям из всех аккаунтов."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"получать доступ к кэшу файловой системы"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Приложение сможет выполнять чтение и запись в файловую систему кэша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Осуществление/прием интернет-вызовов"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Шифровать данные приложений в хранилище."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Отключить камеры"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Запретить использование камер на устройстве."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Домашний"</item>
<item msgid="869923650527136615">"Мобильный"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Сеть VPN подключена: <xliff:g id="SESSION">%s</xliff:g>. Нажмите, чтобы открыть настройки."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Подключение…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Подключено"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ошибка"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Нажмите, чтобы сбросить соединение"</string>
<string name="upload_file" msgid="2897957172366730416">"Выбрать файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не выбран файл"</string>
<string name="reset" msgid="2448168080964209908">"Сбросить"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Воспроизведение звука через Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Перенаправлять поток мультимедиа"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Встроенный экран"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Встроенный экран"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Наложение № <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Экстренный вызов"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Забыли графический ключ?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильный графический ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Неправильный пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильный PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Введите графический ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введите PIN-код SIM-карты"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Введите PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Введите пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-код"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Новый PIN-код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Разблокировка SIM-карты…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неверный PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Введите PIN-код (от 4 до 8 цифр)."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Введите PUK-код из 8 или более цифр."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Введите PUK-код и новый PIN-код"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Введен неверный PUK-код."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Слишком много попыток ввода графического ключа"</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="8849589033659332457">"Проверка…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Не удалось разблокировать устройство с помощью PIN-кода (число неудачных попыток: <xliff:g id="NUMBER_0">%d</xliff:g>). "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Не удалось разблокировать устройство с помощью пароля (число неудачных попыток: <xliff:g id="NUMBER_0">%d</xliff:g>)."\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Не удалось разблокировать устройство с помощью графического ключа (число неудачных попыток: <xliff:g id="NUMBER_0">%d</xliff:g>)."\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Не удалось разблокировать планшетный ПК (число неудачных попыток: <xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого будут установлены настройки по умолчанию, что приведет к удалению всех пользовательских данных."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Не удалось разблокировать телефон (число неудачных попыток: <xliff:g id="NUMBER_0">%d</xliff:g>). Осталось попыток: <xliff:g id="NUMBER_1">%d</xliff:g>. После этого будут установлены настройки по умолчанию, что приведет к удалению всех пользовательских данных."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 6240be3..fdc9c44 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikácii zápis na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"úprava alebo odstránenie obsahu interného ukladacieho priestoru média"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikácii zmeniť obsah interného ukladacieho priestoru média."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"pristupovať k externému ukladaciemu priestoru pre všetkých používateľov"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Umožňuje aplikácii pristupovať k externému ukladaciemu priestoru pre všetkých používateľov."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristupovať do súborového systému vyrovnávacej pamäte"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikácii čítať a zapisovať do súborového systému vyrovnávacej pamäte."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutočňovať a prijímať internetové hovory"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Vyžadovať šifrovanie uložených údajov aplikácií."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Zakázať fotoaparáty"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Zakázať používanie všetkých fotoaparátov zariadenia."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domovská stránka"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Pripojené k relácii <xliff:g id="SESSION">%s</xliff:g>. Po dotyku môžete sieť spravovať."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prebieha pripájanie k vždy zapnutej sieti VPN..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Pripojenie k vždy zapnutej sieti VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba vždy zapnutej siete VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotykom obnovíte pripojenie"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvoliť súbor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nie je vybratý žiadny súbor"</string>
<string name="reset" msgid="2448168080964209908">"Obnoviť"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Hotovo"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Výstup médií"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Vstavaná obrazovka"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Vstavaná obrazovka"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Prekrytie č. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Tiesňové volanie"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nepamätám si vzor"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Nesprávny vzor"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nesprávne heslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Nesprávny kód PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Skúste to znova o <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nakreslite svoj vzor"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Zadajte kód PIN karty SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Zadajte kód PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Zadajte heslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Kód PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nový kód PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Prebieha odomykanie karty SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Nesprávny kód PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Zadajte kód PIN s dĺžkou 4 až 8 číslic."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Zadajte kód PUK, ktorý má 8 alebo viac číslic."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Zadajte kód PUK a nový kód PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Zadaný kód PUK nie je správny."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Príliš veľa pokusov o nakreslenie vzoru"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ak chcete telefón odomknúť, prihláste sa pomocou svojho účtu Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Používateľské meno (e-mail)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Heslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prihlásiť sa"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné používateľské meno alebo heslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zabudli ste svoje používateľské meno alebo heslo?"\n" Navštívte stránky "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Prebieha kontrola..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávny kód PIN. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávne heslo. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v tablete obnovia predvolené továrenské nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER_0">%d</xliff:g>-krát. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa v telefóne obnovia predvolené továrenské nastavenia a všetky používateľské údaje budú stratené."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V tablete sa teraz obnovia predvolené továrenské nastavenia."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefón ste sa pokúsili odomknúť nesprávnym spôsobom <xliff:g id="NUMBER">%d</xliff:g>-krát. V telefóne sa teraz obnovia predvolené továrenské nastavenia."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po ďalších neúspešných pokusoch (<xliff:g id="NUMBER_1">%d</xliff:g>) budete vyzvaní odomknúť tablet pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 1ede6b7..e429d71 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Programu omogoča pisanje na kartico SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"spreminjanje/brisanje vsebine notranje shrambe nosilca podatkov"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Programu omogoča spreminjanje vsebine notranje shrambe nosilca podatkov."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"dostop do zunanje naprave za shranjevanje za vse uporabnike"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Aplikaciji omogoča dostop do zunanje naprave za shranjevanje za vse uporabnike."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostop do datotečnega sistema predpomnilnika"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Programu omogoča branje in pisanje v datotečni sistem predpomnilnika."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"opravljanje/sprejemanje internetnih klicev"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Shranjeni podatki programa morajo biti šifrirani."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Onemogoči fotoaparate"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Prepreči uporabo vseh fotoaparatov v napravi."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Začetna stran"</item>
<item msgid="869923650527136615">"Mobilni"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktiviral program <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Vzpostavljena povezava s sejo <xliff:g id="SESSION">%s</xliff:g>. Dotaknite se, če želite upravljati omrežje."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezovanje v stalno vklopljeno navidezno zasebno omrežje ..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Vzpostavljena povezava v stalno vklopljeno navidezno zasebno omrežje"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Napaka stalno vklopljenega navideznega zasebnega omrežja"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotaknite se, da ponastavite povezavo"</string>
<string name="upload_file" msgid="2897957172366730416">"Izberi datoteko"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nobena datoteka ni izbrana"</string>
<string name="reset" msgid="2448168080964209908">"Ponastavi"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Zvok prek Bluetootha"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Končano"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Izhod predstavnosti"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Vgrajen zaslon"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Vgrajen zaslon"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Prekrivanje #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Klic v sili"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Pozabljen vzorec"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Napačen vzorec"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Napačno geslo"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Napačen PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Čez <xliff:g id="NUMBER">%d</xliff:g> sekund poskusite znova."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Narišite vzorec"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Vnesite PIN za kartico SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Vnesite PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Vnesite geslo"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Koda PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Nova koda PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Odklepanje kartice SIM ..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Napačna koda PIN."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Vnesite PIN, ki vsebuje od štiri do osem številk."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Vnesite 8- ali več mestni PUK."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Vnesite kodo PUK in novo kodo PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Vneseni PUK ni pravilen."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Preveč poskusov vzorca"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Če želite odkleniti napravo, se prijavite z Google Računom."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Uporabniško ime (e-pošta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Geslo"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neveljavno uporabniško ime ali geslo."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ali ste pozabili uporabniško ime ali geslo?"\n"Obiščite "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Preverjanje ..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Geslo ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%d</xliff:g>-krat. "\n\n"Poskusite znova čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablični računalnik ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefon ste poskusili <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno odkleniti. Če poskusite še <xliff:g id="NUMBER_1">%d</xliff:g>-krat in ne uspete, bo ponastavljen na privzete tovarniške nastavitve in vsi uporabniški podatki bodo izgubljeni."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablični računalnik ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon ste poskusili <xliff:g id="NUMBER">%d</xliff:g>-krat napačno odkleniti, zato bo ponastavljen na privzete tovarniške nastavitve."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da tablični računalnik odklenete z e-poštnim računom."\n\n"Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo."\n\n"Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4ddde21..83eb61e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"измена/брисање интерне меморије медија"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозвољава апликацији да мења садржај интерне меморије медијума."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"приступ спољној меморији свих корисника"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дозвољава апликацији да приступа спољној меморији за све кориснике."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"приступ систему датотека кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозвољава апликацији да чита систем датотека кеша и уписује податке у њега."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"упућивање/пријем Интернет позива"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Захтева да сачувани подаци апликације буду шифровани."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Онемогућавање камера"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Спречите коришћење свих камера уређаја."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Кућа"</item>
<item msgid="869923650527136615">"Мобилни"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Повезано са сесијом <xliff:g id="SESSION">%s</xliff:g>. Додирните да бисте управљали мрежом."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Повезивање стално укљученог VPN-а..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Стално укључени VPN је повезан"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка стално укљученог VPN-а"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Додирните да бисте ресетовали везу"</string>
<string name="upload_file" msgid="2897957172366730416">"Одабери датотеку"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Није изабрана ниједна датотека"</string>
<string name="reset" msgid="2448168080964209908">"Поново постави"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth аудио"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Излаз медија"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Уграђени екран"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Уграђени екран"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Постављени елемент бр. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Хитни позив"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Заборављени шаблон"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Погрешан шаблон"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Погрешна лозинка"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Погрешан PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Покушајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Нацртајте шаблон"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Унесите PIN SIM картице"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Унесите PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Унесите лозинку"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK кôд"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Нови PIN кôд"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Откључавање SIM картице…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN кôд је нетачан."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Унесите PIN који има од 4 до 8 бројева."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Унесите PUK који се састоји од 8 бројева или више."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Унесите PUK и нови PIN кôд"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK који сте унели није тачан."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Превише покушаја уноса шаблона"</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="8849589033659332457">"Проверавање..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте PIN неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте лозинку неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 493a0bb..747a05e 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ändra/ta bort innehåll"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillåter att appen ändrar innehållet på den interna lagringsenheten."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"åtkomst till lagringsutrymme"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Tillåter att appen får åtkomst till en extern lagringsenhet för alla användare."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"åtkomst till cachefilsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillåter att appen läser och skriver till cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"ringa/ta emot Internetsamtal"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Kräv att sparade appdata krypteras."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Inaktivera kameror"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Förhindra att enhetens kameror används."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hem"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveras av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryck om du vill hantera nätverket."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ansluten till <xliff:g id="SESSION">%s</xliff:g>. Knacka lätt om du vill hantera nätverket."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ansluter till Always-on VPN ..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ansluten till Always-on VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fel på Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Tryck för att återställa anslutningen"</string>
<string name="upload_file" msgid="2897957172366730416">"Välj fil"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil har valts"</string>
<string name="reset" msgid="2448168080964209908">"Återställ"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-ljud"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Klar"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medieuppspelning"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Inbyggd skärm"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Inbyggd skärm"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Överlagring #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Nödsamtal"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Har du glömt ditt grafiska lösenord?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Fel grafiskt lösenord"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Fel lösenord"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Fel PIN-kod"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Rita ditt grafiska lösenord"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ange PIN-kod för SIM-kortet"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ange PIN-kod"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ange lösenord"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-kod"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Ny PIN-kod"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Låser upp SIM-kort …"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Fel PIN-kod."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Ange en PIN-kod med 4 till 8 siffror."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Ange en PUK-kod med minst 8 siffror."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Ange PUK-koden och en ny PIN-kod"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK-koden som du angav är felaktig."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"För många försök med grafiskt lösenord"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Logga in med ditt Google-konto om du vill låsa upp."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Användarnamn (e-post)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Lösenord"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logga in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ogiltigt användarnamn eller lösenord."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glömt ditt användarnamn eller lösenord?"\n"Besök "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrollerar …"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 759000d..0daf8b8 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Inaruhusu programu kuandikia kadi ya SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"badilisha/futa maudhui ya hifadhi ya media ya ndani."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Inaruhusu programu kurekebisha maudhui ya hifadhi ya ndani vyombo vya habari."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"Fikia hifadhi ya nje ya watumiaji wote"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Inaruhusu programu kufikia hifadhi ya nje kwa watumiaji wote."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"fikia faili za mfumo za kache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Inaruhusu programu kusoma na kuandika mfumo wa faili wa kache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"piga/pokea simu za mtandao"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Inahitaji kwamba data ya programu iliyohifadhiwa iwe na msimbo fiche."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Lemaza kamera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Zuia matumizi yote ya kamera za kifaa."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Nyumbani"</item>
<item msgid="869923650527136615">"Simu ya mkononi"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Gusa ili kudhibiti mtandao."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gusa ili kudhibiti mtandao."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kila mara VPN iliyowashwa inaunganishwa…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Kila mara VPN iliyowashwa imeunganishwa"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kila mara kuna hitilafu ya VPN iliyowashwa"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Gusa ili kuweka upya muunganisho"</string>
<string name="upload_file" msgid="2897957172366730416">"Chagua faili"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Hakuna faili iliyochaguliwa"</string>
<string name="reset" msgid="2448168080964209908">"Weka upya"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Sauti ya Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Kwisha"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Towe la midia"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Skrini Iliyojengewa ndani"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Skrini Iliyojengwa ndani"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Mpanganisho #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Simu ya dharura"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Umesahau Ruwaza"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Ruwaza Isiyofaa"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Nenosiri Lisilofaa"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Isiyofaa"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena kwa sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Chora ruwaza yako"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingiza SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingiza PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ingiza Nenosiri"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Msimbo wa PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Msimbo mpya wa PIN"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Inafungua kadi ya SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Msimbo wa PIN usio sahihi."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Charaza PIN ambayo ni nambari 4 hadi 8."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Andika PUK ambayo ina urefu wa nambari 8 au zaidi."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Charaza PUK na msimbo mpya wa PIN"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK uliyoicharaza siyo sahihi."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Majaribio mengi mno ya mchoro"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ili kufungua, ingia kwa Akaunti yako ya Google."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Jina la mtumiaji (barua pepe)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Nenosiri"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ingia"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Jina la mtumiaji au nenosiri batili."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Je, umesahau jina lako la mtumiaji au nenosiri?"\n"Tembela "<b>"Bgoogle.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Inakagua..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kimakosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena katika sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kimakosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena kwa sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kimakosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena kwa sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itawekwa upya kwenye kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> zaidi yasiyofaulu, simu itawekwa upya kwenye kiwanda chaguo-msingi na data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itawekwa upya kwenye kiwanda chaguo-msingi."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu itawekwa upya kwenye kiwanda chaguo-msingi."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe."\n\n" Jaribu tena kwa sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe."\n\n" Jaribu tena kwa sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 8937c2a..4e202ac 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -45,6 +45,9 @@
<!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
<dimen name="keyguard_lockscreen_outerring_diameter">364dp</dimen>
+ <!-- Height of FaceUnlock view in keyguard -->
+ <dimen name="face_unlock_height">430dip</dimen>
+
<!-- target placement radius for GlowPadView. Should be 1/2 of outerring diameter. -->
<dimen name="glowpadview_target_placement_radius">182dip</dimen>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index efb1d6f..7ada8ec 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"แก้/ลบเนื้อหาข้อมูลสื่อภายใน"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"อนุญาตให้แอปพลิเคชันแก้ไขเนื้อหาของที่เก็บข้อมูลสื่อภายใน"</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"เข้าถึงที่จัดเก็บภายนอกของผู้ใช้ทั้งหมด"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"อนุญาตให้แอปพลิเคชันเข้าถึงที่จัดเก็บข้อมูลภายนอกสำหรับผู้ใช้ทั้งหมด"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"เข้าถึงระบบไฟล์แคช"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"อนุญาตให้แอปพลิเคชันอ่านและเขียนระบบไฟล์แคช"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"โทรออก/รับสายอินเทอร์เน็ต"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"ข้อมูลของแอปพลิเคชันที่จัดเก็บต้องมีการเข้ารหัส"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"ปิดใช้งานกล้องถ่ายรูป"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"ป้องกันการใช้กล้องถ่ายรูปของอุปกรณ์ทั้งหมด"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"บ้าน"</item>
<item msgid="869923650527136615">"มือถือ"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"เชื่อมต่อกับ <xliff:g id="SESSION">%s</xliff:g> แตะเพื่อจัดการเครือข่าย"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"กำลังเชื่อมต่อ VPN แบบเปิดตลอดเวลา…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"เชื่อมต่อ VPN แบบเปิดตลอดเวลาแล้ว"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"ข้อผิดพลาดของ VPN แบบเปิดตลอดเวลา"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"แตะเพื่อรีเซ็ตการเชื่อมต่อ"</string>
<string name="upload_file" msgid="2897957172366730416">"เลือกไฟล์"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"ไม่ได้เลือกไฟล์ไว้"</string>
<string name="reset" msgid="2448168080964209908">"รีเซ็ต"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"เสียงบลูทูธ"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"เสร็จสิ้น"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"เอาต์พุตสื่อ"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"หน้าจอในตัว"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"หน้าจอในตัว"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"การวางซ้อน #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"หมายเลขฉุกเฉิน"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"ลืมรูปแบบใช่หรือไม่"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"รูปแบบไม่ถูกต้อง"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"รหัสผ่านไม่ถูกต้อง"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN ไม่ถูกต้อง"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"ลองอีกครั้งในอีก <xliff:g id="NUMBER">%d</xliff:g> วินาที"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"วาดรูปแบบของคุณ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"ป้อน PIN ของซิม"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"ป้อน PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"ป้อนรหัสผ่าน"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"รหัส PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"รหัส PIN ใหม่"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"กำลังปลดล็อกซิมการ์ด…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"รหัส PIN ไม่ถูกต้อง"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"พิมพ์ PIN ซึ่งเป็นเลข 4 ถึง 8 หลัก"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"พิมพ์ PUK ซึ่งต้องเป็นตัวเลขอย่างน้อย 8 หลัก"</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"พิมพ์ PUK และรหัส PIN ใหม่"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK ที่คุณพิมพ์ไม่ถูกต้อง"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ลองหลายรูปแบบมากเกินไป"</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="8849589033659332457">"กำลังตรวจสอบ…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาีที"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"คุณพยายามปลดล็อกแท็บเล็ตอย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง แท็บเล็ตจะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"คุณพยายามปลดล็อกโทรศัพท์อย่างไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากพยายามแล้วไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง โทรศัพท์จะถูกรีเซ็ตเป็นค่าเริ่มต้นจากโรงงานและข้อมูลผู้ใช้ทั้งหมดจะหายไป"</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 261a0b9..91d7d10 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pinapayagan ang app na magsulat sa SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"baguhin/tanggalin ang mga nilalaman ng panloob na imbakan ng media"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pinapayagan ang app na baguhin ang mga nilalaman ng panloob na media storage."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"i-access panlabas na storage ng user"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Pinapayagan ang app na mag-access ng panlabas na storage para sa lahat ng user."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"i-access ang cache filesystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pinapayagan ang app na basahin at isulat ang cache filesystem."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"gumawa/tumanggap ng mga tawag sa Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Hilinging naka-encrypt ang nakaimbak na data ng app."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Huwag paganahin mga camera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Pigilan ang paggamit sa lahat ng camera ng device."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Home"</item>
<item msgid="869923650527136615">"Mobile"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Nakakonekta sa <xliff:g id="SESSION">%s</xliff:g>. Pindutin upang pamahalaan ang network."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kumukonekta ang Always-on VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Nakakonekta ang Always-on VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error sa Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pindutin upang i-reset ang koneksyon"</string>
<string name="upload_file" msgid="2897957172366730416">"Pumili ng file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Walang napiling file"</string>
<string name="reset" msgid="2448168080964209908">"I-reset"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio sa Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Tapos na"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Output ng media"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Built-in na Screen"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Built-in na Screen"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Overlay #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Emergency na tawag"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Nakalimutan ang Pattern"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Maling Pattern"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Maling Password"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Maling PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Subukang muli sa loob ng <xliff:g id="NUMBER">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Iguhit ang iyong pattern"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ilagay ang SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Ilagay ang PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Ilagay ang Password"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK code"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Bagong PIN code"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ina-unlock ang SIM card…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Hindi tamang PIN code."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Mag-type ng PIN na 4 hanggang 8 numero."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Mag-type ng PUK na may 8 numbero o mas mahaba."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"I-type ang PUK at bagong PIN code"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Hindi tama ang na-type mong PUK."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Masyadong maraming pagtatangka sa pattern"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Upang i-unlock, mag-sign in gamit ang iyong Google account."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Username (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Mag-sign in"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Di-wastong username o password."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nakalimutan ang iyong username o password?"\n"Bisitahin ang "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Sinusuri…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Na-type mo nang hindi tama ang iyong PIN nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tinangka mo sa hindi tamang paraan na i-unlock ang tabelt nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang tablet sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, ire-reset ang telepono sa factory default at mawawala ang lahat ng data ng user."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tinangka mo sa hindi tamang paraan na i-unlock ang tablet nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang tablet sa factory default."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Tinangka mo sa hindi tamang paraan na i-unlock ang telepono nang <xliff:g id="NUMBER">%d</xliff:g> (na) beses. Ire-reset na ngayon ang telepono sa factory default."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang tablet mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c60535f..64e665b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"dahili medya depolama birimi içeriğini değiştir/sil"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Uygulamaya, dahili medya depolama içeriğini değiştirme izni verir."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"tüm kullanıcılar için harici depolama eriş"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Uygulamaya tüm kullanıcılar için harici depolamaya erişim izni verir."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"önbellek dosya sistemine eriş"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Uygulamaya, önbellek dosya sisteminde okuma ve yazma yapma izni verir."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"İnternet çağrılar yap/alma"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Depolanan uygulama verilerinin şifrelenmiş olmasını zorunlu kılma."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Kameraları devre dışı bırak"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Tüm cihaz kameralarının kullanımını engelleme."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Ev"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> oturumuna bağlandı. Ağı yönetmek için dokunun."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Her zaman açık VPN\'ye bağlanılıyor…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Her zaman açık VPN\'ye bağlanıldı"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Her zaman açık VPN hatası"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Bağlantıyı sıfırlamak için dokunun"</string>
<string name="upload_file" msgid="2897957172366730416">"Dosya seç"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Seçili dosya yok"</string>
<string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth ses"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Tamamlandı"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Medya çıkışı"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Yerleşik Ekran"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Yerleşik Ekran"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Yer Paylaşımı No. <xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Acil durum çağrısı"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Deseni Unuttunuz mu?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Yanlış Desen"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Yanlış Şifre"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Yanlış PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"<xliff:g id="NUMBER">%d</xliff:g> saniye içinde yeniden deneyin."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Deseninizi çizin"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"SIM PIN kodunu girin"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"PIN\'i girin"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Şifreyi Girin"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK kodu"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Yeni PIN kodu"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"SIM kart kilidi açılıyor…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Yanlış PIN kodu."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"4-8 rakamdan oluşan bir PIN girin."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"8 veya daha uzun basamaklı bir PUK kodu yazın."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"PUK ve yeni PIN kodunu yazın"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Yazdığınız PUK doğru değil."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Çok fazla sayıda desen denemesi yapıldı"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Kilidi açmak için Google hesabınızla oturum açın."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Kullanıcı adı (e-posta)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Şifre"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Oturum aç"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Geçersiz kullanıcı adı veya şifre."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kullanıcı adınızı veya şifrenizi mi unuttunuz?"\n<b>"google.com/accounts/recovery"</b>" adresini ziyaret edin."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Kontrol ediliyor…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış yazdınız. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Tablet kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, tablet fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Telefonun kilidini <xliff:g id="NUMBER_0">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. <xliff:g id="NUMBER_1">%d</xliff:g> defa daha başarısız deneme yapılırsa, telefon fabrika varsayılan değerine sıfırlanır ve tüm kullanıcı verileri kaybedilir."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Tablet kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Tablet şimdi fabrika varsayılanına sıfırlanacak."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Telefon kilidini <xliff:g id="NUMBER">%d</xliff:g> defa yanlış bir şekilde açmaya çalıştınız. Telefon şimdi fabrika varsayılanına sıfırlanacak."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra, tabletinizi bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index a8633e8..8a2cf15 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволяє програмі записувати на карту SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змінювати/видаляти вміст внутр. сховища даних"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозволяє програмі змінювати вміст внутрішнього сховища даних."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"доступ до зовн. пам’яті всіх корист."</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Дозволяє програмі отримувати доступ до зовнішньої пам’яті всіх користувачів."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"отр. дост. до файл. сист. кешу"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозволяє програмі читати з файлової системи кеш-пам’яті та писати в неї."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"здійсн./отрим. Інтернет-дзвін."</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Вимагати шифрування даних збереженої програми."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Вимкнути камери"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Запобігати використанню всіх камер пристрою."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Дом."</item>
<item msgid="869923650527136615">"Мобільний"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Під’єднано до сеансу <xliff:g id="SESSION">%s</xliff:g>. Торкніться, щоб керувати мережею."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Під’єднання до постійної мережі VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Під’єднано до постійної мережі VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Помилка постійної мережі VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Торкніться, щоб скинути з’єднання"</string>
<string name="upload_file" msgid="2897957172366730416">"Виберіть файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не вибрано файл"</string>
<string name="reset" msgid="2448168080964209908">"Віднов."</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Аудіо Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Вивід медіа-даних"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Вбудований екран"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Вбудований екран"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Накладання №<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Екстрений виклик"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Не пам’ятаю ключ"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Неправильний ключ"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Неправильний пароль"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Неправильний PIN-код"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Повторіть спробу через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Намалюйте ключ"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Введіть PIN-код SIM-карти"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Введіть PIN-код"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Введіть пароль"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK-код"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Новий PIN-код"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Розблокування SIM-карти…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Неправильний PIN-код."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Введіть PIN-код із 4–8 цифр."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Введіть PUK-код із 8 або більше цифр."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Введіть PUK-код і новий PIN-код"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"Введений PUK-код не правильний."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Забагато спроб намалювати ключ"</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="8849589033659332457">"Перевірка…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Кількість невдалих спроб розблокувати планшетний ПК: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування планшетного ПК буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Кількість невдалих спроб розблокувати телефон: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі налаштування телефону буде змінено на заводські за умовчанням, а всі дані користувача – втрачено."</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 012a47a..2c9289d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Cho phép ứng dụng ghi vào thẻ SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sửa đổi/xóa nội dung trên bộ nhớ phương tiện cục bộ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Cho phép ứng dụng sửa đổi nội dung của bộ lưu trữ phương tiện nội bộ."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"truy cập bộ nhớ ngoài của tất cả người dùng"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Cho phép ứng dụng truy cập bộ nhớ ngoài của tất cả người dùng."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"truy cập hệ thống tệp bộ nhớ cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Cho phép ứng dụng đọc và ghi hệ thống tệp bộ nhớ cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"thực hiện/nhận cuộc gọi qua Internet"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Yêu cầu dữ liệu ứng dụng được lưu trữ phải được mã hóa."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Vô hiệu hóa máy ảnh"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Ngăn sử dụng tất cả máy ảnh của thiết bị."</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Nhà riêng"</item>
<item msgid="869923650527136615">"ĐT di động"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Đã kết nối với <xliff:g id="SESSION">%s</xliff:g>. Chạm để quản lý mạng."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Đang kết nối VPN luôn bật…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Đã kết nối VPN luôn bật"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Lỗi VPN luôn bật"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Chạm để đặt lại kết nối"</string>
<string name="upload_file" msgid="2897957172366730416">"Chọn tệp"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Không có tệp nào được chọn"</string>
<string name="reset" msgid="2448168080964209908">"Đặt lại"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Âm thanh Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Xong"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Đầu ra phương tiện"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Màn hình tích hợp"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Màn hình tích hợp"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Lớp phủ #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Cuộc gọi khẩn cấp"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Đã quên hình"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Hình sai"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Mật khẩu sai"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN sai"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Hãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Vẽ hình của bạn"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Nhập PIN của SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Nhập PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Nhập mật khẩu"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Mã PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Mã PIN mới"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Đang mở khóa thẻ SIM…"</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Mã PIN không chính xác."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Nhập mã PIN có từ 4 đến 8 số."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Nhập PUK có từ 8 số trở lên."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Nhập PUK và mã PIN mới"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"PUK bạn đã nhập không đúng."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Quá nhiều lần nhập hình"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Để mở khóa, hãy đăng nhập bằng tài khoản Google của bạn."</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Tên người dùng (email)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Mật khẩu"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Đăng nhập"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Tên người dùng hoặc mật khẩu không hợp lệ."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bạn quên tên người dùng hoặc mật khẩu?"\n"Hãy truy cập "<b>"google.com/accounts/recovery"</b>"."</string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Đang kiểm tra…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mã PIN của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mật khẩu của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, máy tính bảng sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần mở khóa không thành công nữa, điện thoại sẽ được đặt lại về mặc định ban đầu và tất cả dữ liệu người dùng sẽ bị mất."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa máy tính bảng không đúng cách. Bây giờ, máy tính bảng sẽ được đặt lại về mặc định ban đầu."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Bạn đã <xliff:g id="NUMBER">%d</xliff:g> lần mở khóa điện thoại không đúng cách. Bây giờ, điện thoại sẽ được đặt lại về mặc định ban đầu."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa máy tính bảng bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e982c75..5a86940 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/删除内部媒体存储设备的内容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用修改内部媒体存储设备的内容。"</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"访问所有用户的外部存储设备"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"允许应用访问所有用户的外部存储设备。"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"访问缓存文件系统"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允许应用读取和写入缓存文件系统。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"拨打/接听互联网通话"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"要求对存储的应用数据进行加密。"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"停用相机"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"禁止使用所有设备摄像头。"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"住宅"</item>
<item msgid="869923650527136615">"手机"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"“<xliff:g id="APP">%s</xliff:g>”已激活 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"触摸可管理网络。"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"已连接到“<xliff:g id="SESSION">%s</xliff:g>”。触摸可管理网络。"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在连接到始终处于打开状态的 VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已连接到始终处于打开状态的 VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"始终处于打开状态的 VPN 出现错误"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"触摸即可重置连接"</string>
<string name="upload_file" msgid="2897957172366730416">"选择文件"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"未选定任何文件"</string>
<string name="reset" msgid="2448168080964209908">"重置"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"蓝牙音频"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完成"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"媒体输出线路"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"内置屏幕"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"内置屏幕"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"重叠视图 #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"紧急呼救"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘记了图案?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"图案错误"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"密码错误"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 有误"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"绘制您的图案"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"输入 SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"输入 PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"输入密码"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK 码"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"新的 PIN 码"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解锁 SIM 卡..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 码有误。"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"请输入 4 至 8 位数的 PIN。"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"请输入至少 8 位数的 PUK。"</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"请输入 PUK 和新的 PIN 码"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"您输入的 PUK 不正确。"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"图案尝试次数过多"</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="8849589033659332457">"正在检查..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了 PIN。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了密码。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁平板电脑。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,平板电脑就会重置为出厂默认设置,而且所有用户数据都会丢失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地尝试解锁手机。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,手机就会重置为出厂默认设置,而且所有用户数据都会丢失。"</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 36eba9f..1086764 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/刪除內部媒體儲存裝置內容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允許應用程式修改內部媒體儲存空間的內容。"</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"存取外部儲存空間 (所有使用者)"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"允許應用程式存取外部儲存空間 (所有使用者)。"</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"存取快取檔案系統"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允許應用程式讀取及寫入快取檔案系統。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"撥打/接聽網路電話"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"必須為儲存的應用程式資料加密。"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"停用相機"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"禁止使用所有裝置相機。"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"住家電話"</item>
<item msgid="869923650527136615">"行動電話"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"已連線至 <xliff:g id="SESSION">%s</xliff:g>,輕觸即可管理網路。"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在連線至永久連線的 VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已連線至永久連線的 VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"永久連線的 VPN 發生錯誤"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"輕觸即可重設連線"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"藍牙音訊"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完成"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"媒體輸出"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"內建畫面"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"內建畫面"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"第 <xliff:g id="ID">%1$d</xliff:g> 個重疊效果"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"緊急電話"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"忘記圖形"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"圖形錯誤"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"密碼錯誤"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN 錯誤"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"請在 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"畫出圖形"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"輸入 SIM PIN"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"輸入 PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"輸入密碼"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"PUK 碼"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"新 PIN 碼"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解除 SIM 卡鎖定..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 碼不正確。"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"請輸入 4 到 8 碼的 PIN。"</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"請輸入 8 碼以上的 PUK。"</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"輸入 PUK 碼和新 PIN 碼"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"您輸入的 PUK 不正確。"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"圖形嘗試次數過多"</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="8849589033659332457">"檢查中…"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"您嘗試解除這個平板電腦的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,平板電腦將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"您嘗試解除這支手機的鎖定已失敗 <xliff:g id="NUMBER_0">%d</xliff:g> 次,目前還剩 <xliff:g id="NUMBER_1">%d</xliff:g> 次機會。如果失敗次數超過限制,手機將恢復原廠設定,所有使用者資料都會遺失。"</string>
+ <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="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">%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_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 78b287c..4921b3c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -547,6 +547,8 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ivumela insiza ukuthi ibhalele ekhadini le-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"guqula/susa okuqukethwe kwisitoreji semidiya yangaphakathi"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ivumela insiza ukuthi iguqule okuqukethwe kokulondoloza imidiya yangaphakathi."</string>
+ <string name="permlab_sdcardAccessAll" msgid="8150613823900460576">"ukufinyelela isilondolozi sangaphandle sabo bonke abasebenzisi"</string>
+ <string name="permdesc_sdcardAccessAll" msgid="3215208357415891320">"Vumela uhlelo lokusebenza ukufinyelela isilondolozi sangaphandle kubo bonke abasebenzisi."</string>
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"finyelela kunqolobane yesistimu yefayela"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ivumela insiza ukuthi ifunde futhi ibhale isistimu yokufayila amafayela esikhashana."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"yena/thola amakholi e-Inthanethi"</string>
@@ -577,6 +579,10 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Idinga ukuthi idatha yohlelo lokusebenza olugciniwe ibethelwe"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Khubaza amakhamera"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Vimbela ukusetshenziswa kwamadivaysi wonke wamakhamera"</string>
+ <!-- no translation found for policylab_disableKeyguardWidgets (1794894613389073926) -->
+ <skip />
+ <!-- no translation found for policydesc_disableKeyguardWidgets (7254624892984033592) -->
+ <skip />
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Ekhaya"</item>
<item msgid="869923650527136615">"Iselula"</item>
@@ -1167,6 +1173,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"i-VPN ivuswe ngu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Thinta ukuze wengamele inethiwekhi."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ixhumeke ku-.<xliff:g id="SESSION">%s</xliff:g> Thinta ukuze ulawule inethiwekhi."</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"I-VPN ehlala ikhanya iyaxhuma…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"I-VPN ehlala ikhanya ixhunyiwe"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Iphutha le-VPN ehlala ikhanya"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Thinta ukuze usethe kabusha ukuxhuma"</string>
<string name="upload_file" msgid="2897957172366730416">"Khetha ifayela"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ayikho ifayela ekhethiwe"</string>
<string name="reset" msgid="2448168080964209908">"Setha kabusha"</string>
@@ -1311,5 +1321,46 @@
<string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Umsindo we-Bluetooth"</string>
<string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Qedile"</string>
<string name="media_route_button_content_description" msgid="5758553567065145276">"Okukhiphayo kwemidiya"</string>
- <string name="display_manager_built_in_display" msgid="9042666544146043569">"Okwakhelwe ngaphakathi kwesikrini"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Okwakhelwe ngaphakathi kwesikrini"</string>
+ <!-- no translation found for display_manager_hdmi_display_name (1555264559227470109) -->
+ <skip />
+ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Isendlalelo #<xliff:g id="ID">%1$d</xliff:g>"</string>
+ <!-- no translation found for display_manager_overlay_display_title (652124517672257172) -->
+ <skip />
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Ucingo lwezimo eziphuthumayo"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Ukhohlwe iphethini?"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Iphatheni engalungile"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Iphasiwedi engalungile"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Iphinikhodi engalungile"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Zama futhi emasekhondini angu-<xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Dweba iphethini"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Faka iphinikhodi ye-SIM"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Faka iphinikhodi"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Faka iphasiwedi"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="5183097160254244459">"Ikhodi le-PUK"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="597821135578014901">"Iphinikhodi entsha"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Ivula ikhadi le-SIM..."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Iphinikhodi engalungile!"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Thayipha iphinikhodi enezinombolo ezingu-4 kuya kwezingu-8."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="5216603185442368307">"Thayipha i-PUK enezinombolo ezingu-8 noma ngaphezu."</string>
+ <string name="kg_sim_puk_recovery_hint" msgid="5577753137718442566">"Faka i-PUK nephinikhodi entsha"</string>
+ <string name="kg_invalid_puk" msgid="5809955359950817326">"I-PUK oyithayiphile ayilungile."</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Kunemizamo eminingi kakhulu yephathini!"</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Ukuvula, ngena ngemvumekwi-akhawunti ye-Google"</string>
+ <string name="kg_login_username_hint" msgid="5718534272070920364">"Igama lomsebenzisi (i-imeyli)"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"Iphasiwedi"</string>
+ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ngena ngemvume"</string>
+ <string name="kg_login_invalid_input" msgid="5754664119319872197">"Igama lomsebezisi elingalungile noma iphasiwedi."</string>
+ <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ukhohlwe igama lomsebenzisi noma iphasiwedi?"\n"Vakashela"<b>"google.com/accounts/recovery"</b></string>
+ <string name="kg_login_checking_password" msgid="8849589033659332457">"Iyahlola..."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ubhale iphinikhodi ykho ngendlela engafanele izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Uzame ngokusebenzisa indlela engafanele ukuvula ithebhulethi izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ithebhulethi izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Uzame ngokusebenzisa indlela engafanele ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Ngemuva kokuzama ngaphandle kwempumelelo okungu-<xliff:g id="NUMBER_1">%d</xliff:g>, ifoni izobuyiselwa kwizimiso zasembonini futhi yonke imininingwane yomsebenzisi izolahleka."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ithebhulethi manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Uzame ukuvula ngendlela engafanele ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Ifoni manje isizosethwa kabusha ibe yizimiso ezizenzakalelayo."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Udwebe ngokungalungile iphathini yakho yokuvula izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. Emva <xliff:g id="NUMBER_1">%d</xliff:g> kweminye imizamo engaphumelelanga, uzocelwa ukuvula ithebhulethi yakho usebenzisa ukungena ngemvume kwi-Google."\n\n" Sicela uzame futhi kwengu-<xliff:g id="NUMBER_2">%d</xliff:g> imizuzwana."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google"\n\n" Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%d</xliff:g> imizuzwana."</string>
+ <string name="kg_temp_back_string" msgid="5812983904056640466">"<"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5d8d397..e6fd538 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -768,11 +768,15 @@
<attr name="dialogCustomTitleDecorLayout" format="reference" />
<!-- Window decor layout to use in dialog mode with title only -->
<attr name="dialogTitleDecorLayout" format="reference" />
+
<!-- Theme to use for alert dialogs spawned from this theme. -->
<attr name="alertDialogTheme" format="reference" />
<!-- Icon drawable to use for alerts -->
<attr name="alertDialogIcon" format="reference" />
+ <!-- Theme to use for presentations spawned from this theme. -->
+ <attr name="presentationTheme" format="reference" />
+
<!-- Drawable to use for generic vertical dividers. -->
<attr name="dividerVertical" format="reference" />
@@ -2370,6 +2374,12 @@
<!-- The extra value of the subtype. This string can be any string and will be passed to
the IME when the framework calls the IME with the subtype. -->
<attr name="imeSubtypeExtraValue" format="string" />
+ <!-- The unique id for the subtype. The input method framework keeps track of enabled
+ subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even
+ if other attributes are different. If the ID is unspecified (by calling the other
+ constructor or 0. Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead. -->
+ <attr name="subtypeId" format="integer"/>
</declare-styleable>
<!-- Use <code>spell-checker</code> as the root tag of the XML resource that
@@ -4069,6 +4079,10 @@
The clip will be based on the horizontal gravity: a left gravity will clip the right
edge, a right gravity will clip the left edge, and neither will clip both edges. -->
<flag name="clip_horizontal" value="0x08" />
+ <!-- Push object to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003" />
+ <!-- Push object to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005" />
</attr>
<!-- Reference to a drawable resource to draw with the specified scale. -->
<attr name="drawable" />
@@ -5706,4 +5720,22 @@
<attr name="minHeight" />
</declare-styleable>
+ <!-- PagedView specific attributes. These attributes are used to customize
+ a PagedView view in XML files. -->
+ <declare-styleable name="PagedView">
+ <!-- A spacing override for the icons within a page -->
+ <attr name="pageLayoutWidthGap" format="dimension" />
+ <attr name="pageLayoutHeightGap" format="dimension" />
+ <!-- The padding of the pages that are dynamically created per page -->
+ <attr name="pageLayoutPaddingTop" format="dimension" />
+ <attr name="pageLayoutPaddingBottom" format="dimension" />
+ <attr name="pageLayoutPaddingLeft" format="dimension" />
+ <attr name="pageLayoutPaddingRight" format="dimension" />
+ <!-- The space between adjacent pages of the PagedView. -->
+ <attr name="pageSpacing" format="dimension" />
+ <!-- The padding for the scroll indicator area -->
+ <attr name="scrollIndicatorPaddingLeft" format="dimension" />
+ <attr name="scrollIndicatorPaddingRight" format="dimension" />
+ </declare-styleable>
+
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1c3318d..3a69937 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -639,6 +639,8 @@
physical screen size has changed such as switching to an external
display. -->
<flag name="smallestScreenSize" value="0x0800" />
+ <!-- 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
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 86d03ad..bba2252 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -471,6 +471,9 @@
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">4</integer>
+ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
+ <integer name="config_shutdownBatteryTemperature">680</integer>
+
<!-- Display low battery warning when battery level dips to this value -->
<!-- Display low battery warning when battery level dips to this value -->
<integer name="config_lowBatteryWarningLevel">15</integer>
@@ -479,7 +482,7 @@
<integer name="config_lowBatteryCloseWarningLevel">20</integer>
<!-- Default color for notification LED. -->
- <color name="config_defaultNotificationColor">#ff00ff00</color>
+ <color name="config_defaultNotificationColor">#ffffffff</color>
<!-- Default LED on time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOn">500</integer>
@@ -916,6 +919,9 @@
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
<bool name="config_cellBroadcastAppLinks">false</bool>
+ <!-- The default value if the SyncStorageEngine should sync automatically or not -->
+ <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
+
<!-- Maximum number of supported users -->
<integer name="config_multiuserMaximumUsers">10</integer>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f30943a..3be3b0b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -193,6 +193,9 @@
<!-- Padding on left margin of PIN text entry field to center it when del button is showing -->
<dimen name="keyguard_lockscreen_pin_margin_left">40dip</dimen>
+ <!-- Height of FaceUnlock view in keyguard -->
+ <dimen name="face_unlock_height">330dip</dimen>
+
<!-- Minimum popup width for selecting an activity in ActivityChooserDialog/ActivityChooserView. -->
<dimen name="activity_chooser_popup_min_width">200dip</dimen>
@@ -237,4 +240,20 @@
<dimen name="notification_title_text_size">18dp</dimen>
<!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) -->
<dimen name="notification_subtext_size">12dp</dimen>
+
+ <!-- Keyguard dimensions -->
+ <!-- Width of security view in keyguard. -->
+ <dimen name="kg_security_view_width">500dp</dimen>
+
+ <!-- Height of security view in keyguard. -->
+ <dimen name="kg_security_view_height">0dp</dimen>
+
+ <!-- Width of widget view in keyguard. -->
+ <dimen name="kg_widget_view_width">0dp</dimen>
+
+ <!-- Height of widget view in keyguard. -->
+ <dimen name="kg_widget_view_height">0dp</dimen>
+
+ <!-- Padding surrounding each widget page -->
+ <dimen name="kg_widget_page_padding">10dp</dimen>
</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
new file mode 100644
index 0000000..7834997
--- /dev/null
+++ b/core/res/res/values/integers.xml
@@ -0,0 +1,22 @@
+<?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.
+*/
+-->
+<resources>
+ <integer name="kg_security_flip_duration">150</integer>
+ <integer name="kg_security_fade_duration">150</integer>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d761980..4db8cd1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -120,6 +120,8 @@
<java-symbol type="id" name="old_app_action" />
<java-symbol type="id" name="old_app_description" />
<java-symbol type="id" name="old_app_icon" />
+ <java-symbol type="id" name="overlay_display_window_texture" />
+ <java-symbol type="id" name="overlay_display_window_title" />
<java-symbol type="id" name="package_label" />
<java-symbol type="id" name="packages_list" />
<java-symbol type="id" name="pause" />
@@ -253,6 +255,7 @@
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
+ <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
<java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
@@ -479,7 +482,10 @@
<java-symbol type="string" name="decline" />
<java-symbol type="string" name="default_text_encoding" />
<java-symbol type="string" name="description_target_unlock_tablet" />
- <java-symbol type="string" name="display_manager_built_in_display" />
+ <java-symbol type="string" name="display_manager_built_in_display_name" />
+ <java-symbol type="string" name="display_manager_hdmi_display_name" />
+ <java-symbol type="string" name="display_manager_overlay_display_name" />
+ <java-symbol type="string" name="display_manager_overlay_display_title" />
<java-symbol type="string" name="double_tap_toast" />
<java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
<java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
@@ -705,6 +711,7 @@
<java-symbol type="string" name="policydesc_setGlobalProxy" />
<java-symbol type="string" name="policydesc_watchLogin" />
<java-symbol type="string" name="policydesc_wipeData" />
+ <java-symbol type="string" name="policydesc_disableKeyguardWidgets" />
<java-symbol type="string" name="policylab_disableCamera" />
<java-symbol type="string" name="policylab_encryptedStorage" />
<java-symbol type="string" name="policylab_expirePassword" />
@@ -714,6 +721,7 @@
<java-symbol type="string" name="policylab_setGlobalProxy" />
<java-symbol type="string" name="policylab_watchLogin" />
<java-symbol type="string" name="policylab_wipeData" />
+ <java-symbol type="string" name="policylab_disableKeyguardWidgets" />
<java-symbol type="string" name="postalTypeCustom" />
<java-symbol type="string" name="postalTypeHome" />
<java-symbol type="string" name="postalTypeOther" />
@@ -1093,6 +1101,7 @@
<java-symbol type="layout" name="list_menu_item_radio" />
<java-symbol type="layout" name="locale_picker_item" />
<java-symbol type="layout" name="media_controller" />
+ <java-symbol type="layout" name="overlay_display_window" />
<java-symbol type="layout" name="preference" />
<java-symbol type="layout" name="preference_header_item" />
<java-symbol type="layout" name="preference_list_content" />
@@ -1130,6 +1139,8 @@
<java-symbol type="layout" name="notification_template_part_time" />
<java-symbol type="layout" name="notification_template_part_chronometer" />
<java-symbol type="layout" name="notification_template_inbox" />
+ <java-symbol type="layout" name="keyguard_multi_user_avatar" />
+ <java-symbol type="layout" name="keyguard_multi_user_selector_widget" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -1203,6 +1214,10 @@
<java-symbol type="anim" name="dock_left_exit" />
<java-symbol type="anim" name="dock_right_enter" />
<java-symbol type="anim" name="dock_right_exit" />
+ <java-symbol type="anim" name="keyguard_security_animate_in" />
+ <java-symbol type="anim" name="keyguard_security_animate_out" />
+ <java-symbol type="anim" name="keyguard_security_fade_in" />
+ <java-symbol type="anim" name="keyguard_security_fade_out" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
<java-symbol type="array" name="config_longPressVibePattern" />
<java-symbol type="array" name="config_safeModeDisabledVibePattern" />
@@ -1230,6 +1245,7 @@
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
<java-symbol type="dimen" name="status_bar_height" />
+ <java-symbol type="dimen" name="kg_widget_page_padding" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1245,8 +1261,11 @@
<java-symbol type="drawable" name="jog_tab_right_sound_on" />
<java-symbol type="drawable" name="jog_tab_target_green" />
<java-symbol type="drawable" name="jog_tab_target_yellow" />
+ <java-symbol type="drawable" name="magnified_region_frame" />
<java-symbol type="drawable" name="menu_background" />
<java-symbol type="drawable" name="stat_sys_secure" />
+ <java-symbol type="drawable" name="kg_widget_overscroll_layer_left" />
+ <java-symbol type="drawable" name="kg_widget_overscroll_layer_right" />
<java-symbol type="id" name="action_mode_bar_stub" />
<java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="backspace" />
@@ -1259,7 +1278,7 @@
<java-symbol type="id" name="date" />
<java-symbol type="id" name="eight" />
<java-symbol type="id" name="emergencyCallButton" />
- <java-symbol type="id" name="faceLockAreaView" />
+ <java-symbol type="id" name="face_unlock_area_view" />
<java-symbol type="id" name="five" />
<java-symbol type="id" name="forgotPatternButton" />
<java-symbol type="id" name="four" />
@@ -1282,7 +1301,7 @@
<java-symbol type="id" name="passwordEntry" />
<java-symbol type="id" name="pinDel" />
<java-symbol type="id" name="pinDisplay" />
- <java-symbol type="id" name="propertyOf" />
+ <java-symbol type="id" name="owner_info" />
<java-symbol type="id" name="pukDel" />
<java-symbol type="id" name="pukDisplay" />
<java-symbol type="id" name="right_icon" />
@@ -1299,6 +1318,33 @@
<java-symbol type="id" name="two" />
<java-symbol type="id" name="unlock_widget" />
<java-symbol type="id" name="zero" />
+ <java-symbol type="id" name="keyguard_message_area" />
+ <java-symbol type="id" name="keyguard_click_area" />
+ <java-symbol type="id" name="keyguard_selector_view" />
+ <java-symbol type="id" name="keyguard_pattern_view" />
+ <java-symbol type="id" name="keyguard_password_view" />
+ <java-symbol type="id" name="keyguard_face_unlock_view" />
+ <java-symbol type="id" name="keyguard_sim_pin_view" />
+ <java-symbol type="id" name="keyguard_sim_puk_view" />
+ <java-symbol type="id" name="keyguard_account_view" />
+ <java-symbol type="id" name="app_widget_container" />
+ <java-symbol type="id" name="view_flipper" />
+ <java-symbol type="id" name="emergency_call_button" />
+ <java-symbol type="id" name="keyguard_host_view" />
+ <java-symbol type="id" name="delete_button" />
+ <java-symbol type="id" name="lockPatternView" />
+ <java-symbol type="id" name="forgot_password_button" />
+ <java-symbol type="id" name="glow_pad_view" />
+ <java-symbol type="id" name="sim_pin_entry" />
+ <java-symbol type="id" name="delete_button" />
+ <java-symbol type="id" name="sim_puk_entry" />
+ <java-symbol type="id" name="sim_pin_entry" />
+ <java-symbol type="id" name="puk_delete_button" />
+ <java-symbol type="id" name="pin_delete_button" />
+ <java-symbol type="id" name="keyguard_user_avatar" />
+ <java-symbol type="id" name="keyguard_user_name" />
+ <java-symbol type="id" name="keyguard_active_user" />
+ <java-symbol type="id" name="keyguard_inactive_users" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />
@@ -1306,6 +1352,7 @@
<java-symbol type="integer" name="config_lidNavigationAccessibility" />
<java-symbol type="integer" name="config_lidOpenRotation" />
<java-symbol type="integer" name="config_longPressOnHomeBehavior" />
+ <java-symbol type="integer" name="kg_security_flip_duration" />
<java-symbol type="layout" name="global_actions_item" />
<java-symbol type="layout" name="global_actions_silent_mode" />
<java-symbol type="layout" name="keyguard_screen_glogin_unlock" />
@@ -1328,6 +1375,7 @@
<java-symbol type="layout" name="screen_simple_overlay_action_mode" />
<java-symbol type="layout" name="screen_title" />
<java-symbol type="layout" name="screen_title_icons" />
+ <java-symbol type="layout" name="keyguard_host_view" />
<java-symbol type="string" name="abbrev_wday_month_day_no_year" />
<java-symbol type="string" name="android_upgrading_title" />
<java-symbol type="string" name="bugreport_title" />
@@ -1381,6 +1429,38 @@
<java-symbol type="style" name="Animation.LockScreen" />
<java-symbol type="style" name="Theme.Dialog.RecentApplications" />
<java-symbol type="style" name="Theme.ExpandedMenu" />
+ <java-symbol type="string" name="kg_emergency_call_label" />
+ <java-symbol type="string" name="kg_forgot_pattern_button_text" />
+ <java-symbol type="string" name="kg_wrong_pattern" />
+ <java-symbol type="string" name="kg_wrong_password" />
+ <java-symbol type="string" name="kg_wrong_pin" />
+ <java-symbol type="string" name="kg_too_many_failed_attempts_countdown" />
+ <java-symbol type="string" name="kg_pattern_instructions" />
+ <java-symbol type="string" name="kg_sim_pin_instructions" />
+ <java-symbol type="string" name="kg_pin_instructions" />
+ <java-symbol type="string" name="kg_password_instructions" />
+ <java-symbol type="string" name="kg_puk_enter_puk_hint" />
+ <java-symbol type="string" name="kg_puk_enter_pin_hint" />
+ <java-symbol type="string" name="kg_sim_unlock_progress_dialog_message" />
+ <java-symbol type="string" name="kg_password_wrong_pin_code" />
+ <java-symbol type="string" name="kg_invalid_sim_pin_hint" />
+ <java-symbol type="string" name="kg_invalid_sim_puk_hint" />
+ <java-symbol type="string" name="kg_sim_puk_recovery_hint" />
+ <java-symbol type="string" name="kg_invalid_puk" />
+ <java-symbol type="string" name="kg_login_too_many_attempts" />
+ <java-symbol type="string" name="kg_login_instructions" />
+ <java-symbol type="string" name="kg_login_username_hint" />
+ <java-symbol type="string" name="kg_login_password_hint" />
+ <java-symbol type="string" name="kg_login_submit_button" />
+ <java-symbol type="string" name="kg_login_invalid_input" />
+ <java-symbol type="string" name="kg_login_account_recovery_hint" />
+ <java-symbol type="string" name="kg_login_checking_password" />
+ <java-symbol type="string" name="kg_too_many_failed_pin_attempts_dialog_message" />
+ <java-symbol type="string" name="kg_too_many_failed_pattern_attempts_dialog_message" />
+ <java-symbol type="string" name="kg_too_many_failed_password_attempts_dialog_message" />
+ <java-symbol type="string" name="kg_failed_attempts_almost_at_wipe" />
+ <java-symbol type="string" name="kg_failed_attempts_now_wiping" />
+ <java-symbol type="string" name="kg_failed_attempts_almost_at_login" />
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
@@ -1476,6 +1556,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
+ <java-symbol type="integer" name="config_shutdownBatteryTemperature" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
@@ -3692,5 +3773,7 @@
<public type="attr" name="listPreferredItemPaddingStart" />
<public type="attr" name="listPreferredItemPaddingEnd" />
<public type="attr" name="singleUser" />
-
+ <public type="attr" name="presentationTheme" />
+ <public type="attr" name="subtypeId"/>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e77dde7..9f254b6 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -669,8 +669,15 @@
<string name="permlab_filter_events">filter events</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_filter_events">Allows an application to register an input filter
- which filters the stream of all user events before they are dispatched. Malicious app
- may control the system UI whtout user intervention.</string>
+ which filters the stream of all user events before they are dispatched. Malicious app
+ may control the system UI whtout user intervention.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_magnify_display">magnify display</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_magnify_display">Allows an application to magnify the content of a
+ display. Malicious apps may transform the display content in a way that renders the
+ device unusable.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_shutdown">partial shutdown</string>
@@ -1616,6 +1623,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
<string name="permdesc_mediaStorageWrite" product="default">Allows the app to modify the contents of the internal media storage.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
+ <string name="permlab_sdcardAccessAll">access external storage of all users</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_sdcardAccessAll">Allows the app to access external storage for all users.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_cache_filesystem">access the cache filesystem</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1688,6 +1700,10 @@
<string name="policylab_disableCamera">Disable cameras</string>
<!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
<string name="policydesc_disableCamera">Prevent use of all device cameras.</string>
+ <!-- Title of policy access to disable all device cameras [CHAR LIMIT=30]-->
+ <string name="policylab_disableKeyguardWidgets">Disable widgets on keyguard</string>
+ <!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
+ <string name="policydesc_disableKeyguardWidgets">Prevent use of some or all widgets on keyguard.</string>
<!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
<!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
@@ -3651,6 +3667,123 @@
<!-- Display manager service -->
<!-- Name of the built-in display. [CHAR LIMIT=50] -->
- <string name="display_manager_built_in_display">Built-in Screen</string>
+ <string name="display_manager_built_in_display_name">Built-in Screen</string>
+
+ <!-- Name of the HDMI display. [CHAR LIMIT=50] -->
+ <string name="display_manager_hdmi_display_name">HDMI Screen</string>
+
+ <!-- Name of the N'th overlay display for testing. [CHAR LIMIT=50] -->
+ <string name="display_manager_overlay_display_name">Overlay #<xliff:g id="id">%1$d</xliff:g></string>
+
+ <!-- Title text to show within the overlay. [CHAR LIMIT=50] -->
+ <string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
+
+ <!-- Keyguard strings -->
+ <!-- Label shown on emergency call button in keyguard -->
+ <string name="kg_emergency_call_label">Emergency call</string>
+ <!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
+ <string name="kg_forgot_pattern_button_text">Forgot Pattern</string>
+ <!-- Message shown when user enters wrong pattern -->
+ <string name="kg_wrong_pattern">Wrong Pattern</string>
+ <!-- Message shown when user enters wrong password -->
+ <string name="kg_wrong_password">Wrong Password</string>
+ <!-- Message shown when user enters wrong PIN -->
+ <string name="kg_wrong_pin">Wrong PIN</string>
+ <!-- Countdown message shown after too many failed unlock attempts -->
+ <string name="kg_too_many_failed_attempts_countdown">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+ <!-- Instructions for using the pattern unlock screen -->
+ <string name="kg_pattern_instructions">Draw your pattern</string>
+ <!-- Instructions for using the SIM PIN unlock screen -->
+ <string name="kg_sim_pin_instructions">Enter SIM PIN</string>
+ <!-- Instructions for using the PIN unlock screen -->
+ <string name="kg_pin_instructions">Enter PIN</string>
+ <!-- Instructions for using the password unlock screen -->
+ <string name="kg_password_instructions">Enter Password</string>
+ <!-- Hint shown in the PUK unlock screen PUK TextView -->
+ <string name="kg_puk_enter_puk_hint">PUK code</string>
+ <!-- Hint shown in the PUK unlock screen PIN TextView -->
+ <string name="kg_puk_enter_pin_hint">New PIN code</string>
+ <!-- Message shown in dialog while the device is unlocking the SIM card -->
+ <string name="kg_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
+ <!-- Message shown when the user enters the wrong PIN code -->
+ <string name="kg_password_wrong_pin_code">Incorrect PIN code.</string>
+ <!-- Message shown when the user enters an invalid SIM pin password in PUK screen -->
+ <string name="kg_invalid_sim_pin_hint">Type a PIN that is 4 to 8 numbers.</string>
+ <!-- Message shown when the user enters an invalid PUK code in the PUK screen -->
+ <string name="kg_invalid_sim_puk_hint">Type a PUK that is 8 numbers or longer.</string>
+ <!-- Instructions for PUK unlock screen -->
+ <string name="kg_sim_puk_recovery_hint">Type PUK and new PIN code</string>
+ <!-- Message shown when the user enters an invalid PUK code -->
+ <string name="kg_invalid_puk">The PUK you typed isn\'t correct.</string>
+ <!-- Message shown when the user exceeds the maximum number of pattern attempts -->
+ <string name="kg_login_too_many_attempts">Too many pattern attempts</string>
+ <!-- Instructions show in account unlock screen allowing user to enter their email password -->
+ <string name="kg_login_instructions">To unlock, sign in with your Google account.</string>
+ <!-- Hint shown in TextView in account unlock screen of keyguard -->
+ <string name="kg_login_username_hint">Username (email)</string>
+ <!-- Hint shown in TextView in account unlock screen of keyguard -->
+ <string name="kg_login_password_hint">Password</string>
+ <!-- Label shown on sign in button on account unlock screen of keyguard -->
+ <string name="kg_login_submit_button">Sign in</string>
+ <!-- Message shown when the user enters an invalid username/password combination in account unlock screen of keyguard -->
+ <string name="kg_login_invalid_input">Invalid username or password.</string>
+ <!-- Hint text shown when user has too many failed password attempts in account unlock screen of keyguard -->
+ <string name="kg_login_account_recovery_hint">Forgot your username or password\?\nVisit <b>google.com/accounts/recovery</b>.</string>
+ <!-- Message shown while device checks username/password in account unlock screen of keyguard -->
+ <string name="kg_login_checking_password">Checking\u2026</string>
+ <!-- Message shown in dialog when max number of attempts are reached for PIN screen of keyguard -->
+ <string name="kg_too_many_failed_pin_attempts_dialog_message">
+ You have incorrectly typed your PIN <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown in dialog when max number of attempts are reached for password screen of keyguard -->
+ <string name="kg_too_many_failed_password_attempts_dialog_message">
+ You have incorrectly typed your password <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ \n\nTry again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet">
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ the tablet will be reset to factory default and all user data will be lost.
+ </string>
+ <!-- Message shown when user is almost at the limit of password attempts where the device will be wiped. -->
+ <string name="kg_failed_attempts_almost_at_wipe" product="default">
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ the phone will be reset to factory default and all user data will be lost.
+ </string>
+ <!-- Message shown in dialog when user has exceeded the maximum attempts and the device will now be wiped -->
+ <string name="kg_failed_attempts_now_wiping" product="tablet">
+ You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times.
+ The tablet will now be reset to factory default.
+ </string>
+ <!-- Message shown in dialog when user has exceeded the maximum attempts and the device will now be wiped -->
+ <string name="kg_failed_attempts_now_wiping" product="default">
+ You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times.
+ The phone will now be reset to factory default.
+ </string>
+ <!-- Message shown in dialog when user is almost at the limit where they will be
+ locked out and may have to enter an alternate username/password to unlock the phone -->
+ <string name="kg_failed_attempts_almost_at_login" product="tablet">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ you will be asked to unlock your tablet using an email account.\n\n
+ Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+ <!-- Message shown in dialog when user is almost at the limit where they will be
+ locked out and may have to enter an alternate username/password to unlock the phone -->
+ <string name="kg_failed_attempts_almost_at_login" product="default">
+ You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+ After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+ you will be asked to unlock your phone using an email account.\n\n
+ Try again in <xliff:g id="number">%d</xliff:g> seconds.
+ </string>
+
+ <string name="kg_temp_back_string"> < </string> <!-- TODO: remove this -->
</resources>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 28fed45..d465356 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -673,7 +673,6 @@
</style>
-
<!-- Animation Styles -->
<style name="Animation.DeviceDefault.Activity" parent="Animation.Holo.Activity">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2f93335..215551b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -186,17 +186,24 @@
<item name="windowFixedHeightMinor">0dp</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="dialogTheme">@android:style/Theme.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="alertDialogCenterButtons">true</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert</item>
+ <!-- Presentation attributes (introduced after API level 10 so does not
+ have a special old-style theme. -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_background</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1012,17 +1019,23 @@
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="dialogTheme">@android:style/Theme.Holo.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_dark</item>
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.Holo.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_dark</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1319,17 +1332,23 @@
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
<item name="dialogTheme">@android:style/Theme.Holo.Light.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
- <item name="alertDialogCenterButtons">false</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Light.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
+ <item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_light</item>
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.Holo.Light.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_light</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1663,6 +1682,10 @@
<style name="Theme.Holo.DialogWhenLarge.NoActionBar" parent="@android:style/Theme.Holo.NoActionBar">
</style>
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Holo.Dialog.Presentation" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ </style>
+
<!-- Light holo dialog themes -->
<!-- Holo light theme for dialog windows and activities, which is used by the
@@ -1760,6 +1783,10 @@
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Holo.Light.Dialog.Presentation" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen" >
+ </style>
+
<!-- Default holographic (dark) for windows that want to have the user's selected
wallpaper appear behind them. -->
<style name="Theme.Holo.Wallpaper">
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index eef831f..2a2b9e0 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -87,9 +87,14 @@
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Dialog</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@@ -239,9 +244,14 @@
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@@ -460,6 +470,15 @@
<style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Holo.Light.DialogWhenLarge.NoActionBar" >
</style>
+
+ <!-- DeviceDefault theme for a presentation window on a secondary display. -->
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Holo.Dialog.Presentation">
+ </style>
+
+ <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Holo.Light.Dialog.Presentation">
+ </style>
+
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index d527c0d..f28ba7e 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -26,6 +26,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.UserHandle;
import android.test.FlakyTest;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
@@ -304,9 +305,9 @@
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
addIntermediate("finished-broadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
@@ -318,11 +319,11 @@
public void testClearSticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
ActivityManagerNative.getDefault().unbroadcastIntent(
null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
addIntermediate("finished-unbroadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
@@ -333,10 +334,10 @@
public void testReplaceSticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
intent.putExtra("test", LaunchpadActivity.DATA_2);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
addIntermediate("finished-broadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
@@ -350,7 +351,7 @@
public void testReceiveSticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
runLaunchpad(LaunchpadActivity.BROADCAST_STICKY1);
}
@@ -360,10 +361,10 @@
public void testReceive2Sticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
intent = new Intent(LaunchpadActivity.BROADCAST_STICKY2, null);
intent.putExtra("test", LaunchpadActivity.DATA_2);
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
runLaunchpad(LaunchpadActivity.BROADCAST_STICKY2);
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 1b69daf..a19b9b4 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -173,7 +173,10 @@
<assign-permission name="android.permission.SET_SCREEN_COMPATIBILITY" uid="shell" />
<assign-permission name="android.permission.READ_EXTERNAL_STORAGE" uid="shell" />
<assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" />
-
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="shell" />
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS_FULL" uid="shell" />
+ <assign-permission name="android.permission.MANAGE_USERS" uid="shell" />
+
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_DRM" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 4a75b91..c3da3bf 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -20,44 +20,43 @@
<p>The following pie chart and table is based on the number of Android devices that have accessed
Google Play within a 14-day period ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
+<div class="col-5" style="margin-left:0">
<table>
<tr>
<th>Version</th>
<th>Codename</th>
- <th>API Level</th>
+ <th>API</th>
<th>Distribution</th>
</tr>
<tr><td><a href="/about/versions/android-1.5.html">1.5</a></td><td>Cupcake</td> <td>3</td><td>0.2%</td></tr>
-<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>4.2%</td></tr>
-<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>15.5%</td></tr>
+<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.4%</td></tr>
+<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>3.7%</td></tr>
+<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>14%</td></tr>
<tr><td><a href="/about/versions/android-2.3.html">2.3 - 2.3.2</a>
</td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.3%</td></tr>
<tr><td><a href="/about/versions/android-2.3.3.html">2.3.3 - 2.3.7
- </a></td><!-- Gingerbread --> <td>10</td><td>60.3%</td></tr>
+ </a></td><!-- Gingerbread --> <td>10</td><td>57.2%</td></tr>
<tr><td><a href="/about/versions/android-3.1.html">3.1</a></td>
<td rowspan="2">Honeycomb</td> <td>12</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.8%</td></tr>
+<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.6%</td></tr>
<tr><td><a href="/about/versions/android-4.0.html">4.0 - 4.0.2</a></td>
<td rowspan="2">Ice Cream Sandwich</td><td>14</td><td>0.1%</td></tr>
<tr><td><a href="/about/versions/android-4.0.3.html">4.0.3 - 4.0.4</a></td>
- <!-- ICS --> <td>15</td><td>15.8%</td></tr>
-<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>0.8%</td></tr>
+ <!-- ICS --> <td>15</td><td>20.8%</td></tr>
+<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>1.2%</td></tr>
</table>
-
</div>
-<div class="col-7" style="margin-right:0">
+<div class="col-8" style="margin-right:0">
<img alt=""
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x310&chd=t:0.2,0.5,4.2,15.5,0.3,60.3,0.5,1.8,0.1,15.8,0.8&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0|Android%204.0.3|Android%204.1&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x245&chd=t:4.3,14,57.5,2.1,20.9,1.2&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
</div><!-- end dashboard-panel -->
-<p style="clear:both"><em>Data collected during a 14-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 14-day period ending on September 4, 2012</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -82,9 +81,9 @@
Google Play within a 14-day period ending on the date indicated on the x-axis.</p>
<img alt="" height="250" width="660"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.6,98.4,98.4,98.6,98.5,98.6,98.8,98.7,98.9,99.1,99.1,99.2,98.6|97.6,97.5,97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.1|89.9,90.3,90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,93.9|62.0,63.7,65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,78.4|4.0,4.1,4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.1|2.6,3.0,3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,17.6|0.7,0.8,1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,15.8&chm=b,c3df9b,0,1,0|b,b6dc7d,1,2,0|tAndroid%202.2,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|tAndroid%202.3.3,496c13,3,0,15,,t::-5|b,9ddb3d,3,4,0|b,91da1e,4,5,0|b,80c414,5,6,0|tAndroid%204.0.3,131d02,6,10,15,,t::-5|B,6fad0c,6,7,0&chg=7,25&chdl=Android%201.6|Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.9,98.9,99.0|90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,94.7,94.9,95.3|65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,79.2,80.1,81.1|4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.9,21.2,23.7|3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,18.4,20.7,23.2|1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,16.6,19.0,21.5|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.8,0.9,1.1&chm=b,c3df9b,0,1,0|tAndroid%202.2,6c9729,1,0,15,,t::-5|b,b6dc7d,1,2,0|tAndroid%202.3.3,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|b,9ddb3d,3,4,0|b,91da1e,4,5,0|tAndroid%204.0.3,253a06,5,8,15,,t::-5|b,80c414,5,6,0|B,6fad0c,6,7,0&chg=7,25&chdl=Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3|Android%204.1&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on August 1, 2012</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on September 1, 2012</em></p>
@@ -111,6 +110,14 @@
<h2 id="Screens">Screen Sizes and Densities</h2>
+
+<img alt="" style="float:right;"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.7,6.5,86,2.8" />
+
+
+<img alt="" style="float:right;clear:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=ldpi%7Cmdpi%7Chdpi%7Cxhdpi&chd=t%3A1.6,18.6,53.6,26.2" />
+
<p>This section provides data about the relative number of active devices that have a particular
screen configuration, defined by a combination of screen size and density. To simplify the way that
you design your user interfaces for different screen configurations, Android divides the range of
@@ -132,10 +139,7 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-
-
-<table>
+<table style="width:350px">
<tr>
<th></th>
<th scope="col">ldpi</th>
@@ -144,22 +148,22 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.5%</td> <!-- small/ldpi -->
+<td>1.1%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>1.2%</td> <!-- small/hdpi -->
+<td>1.7%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
-<td>0.5%</td> <!-- normal/ldpi -->
-<td>12.1%</td> <!-- normal/mdpi -->
-<td>55.3%</td> <!-- normal/hdpi -->
-<td>17.4%</td> <!-- normal/xhdpi -->
+<td>0.4%</td> <!-- normal/ldpi -->
+<td>11.4%</td> <!-- normal/mdpi -->
+<td>51.9%</td> <!-- normal/hdpi -->
+<td>22.3%</td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.1%</td> <!-- large/ldpi -->
-<td>2.7%</td> <!-- large/mdpi -->
+<td>2.5%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
-<td>4.5%</td> <!-- large/xhdpi -->
+<td>3.9%</td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
@@ -169,16 +173,7 @@
</tr>
</table>
-
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Large%20/%20xhdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20xhdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A4.7,0.1,2.7,4.5,55.3,0.5,12.1,17.4,1.2,1.5" />
-
-</div>
-
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
@@ -196,6 +191,10 @@
support for any lower version (for example, support for version 2.0 also implies support for
1.1).</p>
+
+<img alt="" style="float:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8&chf=bg,s,00000000" />
+
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
@@ -209,28 +208,21 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-<table>
+<table style="width:350px">
<tr>
<th scope="col">OpenGL ES Version</th>
<th scope="col">Distribution</th>
</tr>
<tr>
<td>1.1 only</th>
-<td>9.3%</td>
+<td>9.2%</td>
</tr>
<tr>
<td>2.0 & 1.1</th>
-<td>90.7%</td>
+<td>90.8%</td>
</tr>
</table>
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.3,90.7&chf=bg,s,00000000" />
-
-</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd
index db56fa4..485a1bb 100644
--- a/docs/html/about/versions/jelly-bean.jd
+++ b/docs/html/about/versions/jelly-bean.jd
@@ -1,29 +1,6 @@
page.title=Android 4.1 for Developers
@jd:body
-
-<!--<style type="text/css">
-#jd-content {
- max-width:1024px;
-}
-#jd-content div.screenshot {
- float:left;
- clear:left;
- padding:15px 30px 15px 0;
-}
-
-</style>
-
-<p></p>
-
-
-<div style="float:right;width:230px;padding:0px 0px 60px 34px;margin-top:-40px">
-<div>
-<img src="{@docRoot}images/android-jellybean-sm.png" xheight="402" width="280">
-</div>
-<p class="image-caption">Find out more about the Jelly Bean features for users at <a href="http://www.android.com">android.com</a></p>
-</div>-->
-
<div style="float:right;width:320px;padding:0px 0px 0px 34px;clear:both">
<div>
<img src="{@docRoot}images/jb-android-4.1.png" height="426" width="390">
@@ -35,23 +12,9 @@
improvements throughout the platform and added great new features
for users and developers. This document provides a glimpse of what's new for developers.
-<p>See the <a href="{@docRoot}about/versions/android-4.1.html">Android 4.1 APIs</a> document for a detailed look at the new developer APIs,</p>
+<p>See the <a href="{@docRoot}about/versions/android-4.1.html">Android 4.1 APIs</a> document for a detailed look at the new developer APIs.</p>
-<!--
-<ul>
- <li><a href="#performance">Fast, Smooth, Responsive</a></li>
- <li><a href="#accessibility">Enhanced Accessibility</a></li>
- <li><a href="#intl">Support for International Users</a></li>
- <li><a href="#ui">Capabilities for Creating Beautiful UI</a></li>
- <li><a href="#input">New Input Types and Capabilities</a></li>
- <li><a href="#graphics">Animation and Graphics</a></li>
- <li><a href="#connectivity">New Types of Connectivity</a></li>
- <li><a href="#media">Media Capabilities</a></li>
- <li><a href="#google">Google APIs and services</a></li>
- </ul>
--->
-
-<p>Find out more about the Jelly Bean features for users at <a href="http://www.android.com/whatsnew">www.android.com</a></p>
+<p>Find out more about the Jelly Bean features for users at <a href="http://www.android.com/whatsnew">www.android.com</a>.</p>
<h2 id="performance">Faster, Smoother, More Responsive</h2>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 9465f18..e812ccb 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -8,7 +8,7 @@
<ul id="nav">
<!-- Walkthrough for Developers -- quick overview of what it's like to develop on Android -->
<!--<li style="color:red">Overview</li> -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/components/index.html">
<span class="en">App Components</span>
@@ -223,7 +223,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/search/adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
</ul>
- </li>
+ </li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/drag-drop.html">
<span class="en">Drag and Drop</span>
</a></li>
@@ -235,6 +235,9 @@
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/apps.html">
<span class="en">Making Applications Accessible</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/checklist.html">
+ <span class="en">Accessibility Developer Checklist</span>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/services.html">
<span class="en">Building Accessibility Services</span>
</a></li>
@@ -382,9 +385,9 @@
</a></li>
</ul>
</li><!-- end of location and sensors -->
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/connectivity/index.html">
<span class="en">Connectivity</span>
@@ -419,10 +422,10 @@
<span class="en">SIP</span>
</a>
</li>
-
+
</ul>
</li><!-- end of connectivity -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/text/index.html">
<span class="en">Text and Input</span>
@@ -439,7 +442,7 @@
</a></li>
</ul>
</li><!-- end of text and input -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/data/index.html">
<span class="en">Data Storage</span>
@@ -457,7 +460,7 @@
</ul>
</li><!-- end of data storage -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/topics/admin/index.html">
<span class="en">Administration</span>
@@ -475,7 +478,7 @@
-->
</ul>
</li><!-- end of administration -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/webapps/index.html">
<span class="en">Web Apps</span>
@@ -498,7 +501,7 @@
</a></li>
</ul>
</li><!-- end of web apps -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/index.html">
<span class="en">Best Practices</span>
@@ -555,13 +558,13 @@
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/index.html">
<span class="en">Google Services</span>
</a></div>
- <ul>
+ <ul>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/google/play/billing/index.html">
@@ -623,7 +626,7 @@
<li><a href="<?cs var:toroot ?>guide/google/play/expansion-files.html">
<span class="en">APK Expansion Files</span></a>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/gcm/index.html">
<span class="en">Google Cloud Messaging</span></a>
@@ -649,9 +652,9 @@
</ul>
</li><!-- end Google Play -->
-
-
-
+
+
+
<!-- this needs to move
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/index.html">
@@ -691,9 +694,9 @@
</a></div>
</li>
</ul>
- </li>
+ </li>
</ul> -->
-
+
<!-- Remove
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/appendix/index.html">
@@ -710,7 +713,7 @@
<li><a href="<?cs var:toroot ?>guide/appendix/g-app-intents.html">
<span class="en">Intents List: Google Apps</span>
</a></li>
-
+
<li><a href="<?cs var:toroot ?>guide/appendix/glossary.html">
<span class="en">Glossary</span>
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index a46f9a7..5a4e03a 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -307,6 +307,7 @@
<li>{@link android.widget.FrameLayout}</li>
<li>{@link android.widget.LinearLayout}</li>
<li>{@link android.widget.RelativeLayout}</li>
+ <li>{@link android.widget.GridLayout}</li>
</ul>
<p>And the following widget classes:</p>
@@ -327,6 +328,9 @@
<p>Descendants of these classes are not supported.</p>
+<p>RemoteViews also supports {@link android.view.ViewStub}, which is an invisible, zero-sized View you can use
+to lazily inflate layout resources at runtime.</p>
+
<h3 id="AddingMargins">Adding margins to App Widgets</h3>
@@ -410,6 +414,25 @@
done.
(See <a href="#Configuring">Creating an App Widget Configuration
Activity</a> below.)</dd>
+
+<dt>
+ {@link android.appwidget.AppWidgetProvider#onAppWidgetOptionsChanged onAppWidgetOptionsChanged()}
+</dt>
+<dd>
+This is called when the widget is first placed and any time the widget is resized. You can use this callback to show or hide content based on the widget's size ranges. You get the size ranges by calling {@link android.appwidget.AppWidgetManager#getAppWidgetOptions getAppWidgetOptions()}, which returns a {@link android.os.Bundle} that includes the following:<br /><br />
+<ul>
+ <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MIN_WIDTH}—Contains
+the lower bound on the current width, in dp units, of a widget instance.</li>
+ <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MIN_HEIGHT}—Contains
+the lower bound on the current height, in dp units, of a widget instance.</li>
+ <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MAX_WIDTH}—Contains
+ the upper bound on the current width, in dp units, of a widget instance.</li>
+ <li>{@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_MAX_HEIGHT}—Contains
+the upper bound on the current width, in dp units, of a widget instance.</li>
+</ul>
+
+This callback was introduced in API Level 16 (Android 4.1). If you implement this callback, make sure that your app doesn't depend on it since it won't be called on older devices.
+</dd>
<dt>{@link android.appwidget.AppWidgetProvider#onDeleted(Context,int[])}</dt>
<dd>This is called every time an App Widget is deleted from the App Widget
host.</dd>
@@ -533,12 +556,13 @@
to receive the App Widget broadcasts directly, you can implement your own
{@link android.content.BroadcastReceiver} or override the
{@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)} callback.
-The four Intents you need to care about are:</p>
+The Intents you need to care about are as follows:</p>
<ul>
<li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE}</li>
<li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DELETED}</li>
<li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_ENABLED}</li>
<li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_DISABLED}</li>
+ <li>{@link android.appwidget.AppWidgetManager#ACTION_APPWIDGET_OPTIONS_CHANGED}</li>
</ul>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index b0d5d6f..b2c95b9 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -329,6 +329,31 @@
indicates the current locale.</p>
</td>
</tr>
+ <tr id="LayoutDirectionQualifier">
+ <td>Layout Direction</td>
+ <td>Examples:<br/>
+ <code>ldrtl</code><br/>
+ <code>ldltr</code><br/>
+ </td>
+ <td><p>The layout direction of your application. {@code ldrtl} means "layout-direction-right-to-left".
+ {@code ldltr} means "layout-direction-left-to-right" and is the default implicit value.
+ </p>
+ <p>This can apply to any resource like layouts or values or drawables.
+ </p>
+ <p>For example, if you want to provide some specific layout for the Arabic language and some
+ generic layout for any other "right-to-left" language (like Persian or Hebrew) then you would have:
+ </p>
+<pre class="classic no-pretty-print">
+res/
+ layout/ <span style="color:black">
+ main.xml </span>(This is the default layout)
+ layout-ar/ <span style="color:black">
+ main.xml </span>(This is the specific layout for Arabic)
+ layout-ldrtl/ <span style="color:black">
+ main.xml </span>(This applies to any "right-to-left" language, except for Arabic, because the ar language qualifier has a higher precedence.)
+</pre>
+ </td>
+ </tr>
<tr id="SmallestScreenWidthQualifier">
<td>smallestWidth</td>
<td><code>sw<N>dp</code><br/><br/>
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
index d23512b..13b4538 100644
--- a/docs/html/guide/topics/ui/accessibility/apps.jd
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -21,14 +21,11 @@
<li><a href="#accessibility-methods">Implementing accessibility API methods</a></li>
<li><a href="#send-events">Sending accessibility events</a></li>
<li><a href="#populate-events">Populating accessibility events</a></li>
+ <li><a href="#virtual-hierarchy">Providing a customized accessibility context</a></li>
+ <li><a href="#custom-touch-events">Handling custom touch events</a></li>
</ol>
</li>
- <li><a href="#test">Testing Accessibility</a>
- <ol>
- <li><a href="#test-audibles">Testing audible feedback</a></li>
- <li><a href="#test-navigation">Testing focus navigation</a></li>
- </ol>
- </li>
+ <li><a href="#test">Testing Accessibility</a></li>
</ol>
<h2>Key classes</h2>
@@ -42,60 +39,79 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
- <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a>
- </li>
- <li><a href="{@docRoot}design/index.html">Android Design</a></li>
+ <li><a href="checklist.html">Accessibility Developer Checklist</a><li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a><li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
</div>
-<p>Applications built for Android are accessible to users with visual, physical or age-related
-disabilities when they activate accessibility features and services on a device. By default,
-these services make your application more accessible. However, there are further steps you should
-take to optimize the accessibility of your application and ensure a pleasant experience for all your
-users.</p>
+<p>Applications built for Android are more accessible to users with visual, physical or age-related
+limitations when those users activate accessibility services and features on a device. These
+services make your application more accessible even if you do not make any accessibility changes
+to your code. However, there are steps you should take to optimize the accessibility of your
+application and ensure a pleasant experience for all your users.</p>
-<p>Making sure your application is accessible to all users is relatively easy, particularly when you
-use framework-provided user interface components. If you only use these standard components for your
-application, there are just a few steps required to ensure your application is accessible:</p>
+<p>Making sure your application is accessible to all users requires only a few steps, particularly
+when you create your user interface with the components provided by the Android framework. If you
+use only the standard components for your application, the steps are:</p>
<ol>
- <li>Label your {@link android.widget.ImageButton}, {@link android.widget.ImageView}, {@link
-android.widget.EditText}, {@link android.widget.CheckBox} and other user interface controls using
-the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
- {@code android:contentDescription}</a> attribute.</li>
- <li>Make all of your user interface elements accessible with a directional controller,
- such as a trackball or D-pad.</li>
- <li>Test your application by turning on accessibility services like TalkBack and Explore by
- Touch, and try using your application using only directional controls.</li>
+ <li>Add descriptive text to user interface controls in your application using the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> attribute. Pay particular attention to
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox}.</li>
+ <li>Make sure that all user interface elements that can accept input (touches or typing) can be
+ reached with a directional controller, such as a trackball, D-pad (physical or virtual) or
+ navigation <a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">gestures
+ </a>.</li>
+ <li>Make sure that audio prompts are always accompanied by another visual prompt or notification,
+ to assist users who are deaf or hard of hearing.</li>
+ <li>Test your application using only accessibility navigation services and features. Turn on
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-talkback">TalkBack</a> and
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-ebt">Explore by Touch</a>,
+ and then try using your application using only directional controls. For more information on
+ testing for accessibility, see the <a href="{@docRoot}tools/testing/testing_accessibility.html">
+ Accessibility Testing Checklist</a>.</li>
</ol>
-<p>Developers who create custom controls that extend from the {@link android.view.View} class have
-some additional responsibilities for making sure their components are accessible for users. This
-document also discusses how to make custom view controls compatible with accessibility services.</p>
+<p>If you build custom controls that extend the {@link android.view.View} class, you must complete
+some additional work to make sure your components are accessible. This document discusses how to
+make custom view controls compatible with accessibility services.</p>
+
+<p class="note">
+<strong>Note:</strong> The implementation steps in this document describe the requirements for
+making your application accessible for users with blindness or low-vision. Be sure to review the
+requirements for serving users who are deaf and hard of hearing in the
+<a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">Accessibility Developer
+Checklist</a></p>.
+
<h2 id="label-ui">Labeling User Interface Elements</h2>
-<p>Many user interface controls rely on visual cues to inform users of their meaning. For
+<p>Many user interface controls depend on visual cues to indicate their meaning and usage. For
example, a note-taking application might use an {@link android.widget.ImageButton} with a
-picture of a plus sign to indicate that the user can add a new note. Or, an {@link
-android.widget.EditText} component may have a label near it that indicates its purpose. When a user
-with impaired vision accesses your application, these visual cues are often useless.</p>
+picture of a plus sign to indicate that the user can add a new note. An {@link
+android.widget.EditText} component may have a label near it that indicates its purpose. A user
+with impaired vision can't see these cues well enough to follow them, which makes them useless.</p>
-<p>To provide textual information about interface controls (as an alternative to the visual cues),
-use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
-{@code android:contentDescription}</a> attribute. The text you provide in this attribute is not
-visible on the screen, but if a user has enabled accessibility services that provide audible
-prompts, then the description in this attribute is read aloud to the user.</p>
+<p>You can make these controls more accessible with the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+{@code android:contentDescription}</a> XML layout attribute. The text in this attribute does not
+appear on screen, but if the user enables accessibility services that provide audible prompts, then
+when the user navigates to that control, the text is spoken.</p>
-<p>Set the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+<p>For this reason, set the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
{@code android:contentDescription}</a> attribute for every {@link android.widget.ImageButton},
-{@link android.widget.ImageView}, {@link android.widget.EditText}, {@link android.widget.CheckBox}
-in your application's user interface, and on any other input controls that might require additional
-information for users who are not able to see it.</p>
+{@link android.widget.ImageView}, {@link android.widget.CheckBox}
+in your application's user interface, and add descriptions to any other input controls that might
+require additional information for users who are not able to see it.</p>
<p>For example, the following {@link android.widget.ImageButton} sets the content description for
the plus button to the {@code add_note} string resource, which could be defined as “Add note" for an
@@ -108,32 +124,38 @@
android:contentDescription=”@string/add_note”/>
</pre>
-<p>By including the description, speech-based accessibility services can announce "Add note" when a
-user moves focus to this button or hovers over it.</p>
+<p>By including the description, an accessibility service that provides spoken feedback can announce
+"Add note" when a user moves focus to this button or hovers over it.</p>
<p class="note"><strong>Note:</strong> For {@link android.widget.EditText} fields, provide an
<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
-attribute to help users understand what content is expected.</p>
+attribute <em>instead</em> of a content description, to help users understand what content is
+expected when the text field is empty. When the field is filled, TalkBack reads the entered
+content to the user, instead of the hint text.</p>
+
<h2 id="focus-nav">Enabling Focus Navigation</h2>
<p>Focus navigation allows users with disabilities to step through user interface controls using a
-directional controller. Directional controllers can be physical, such as a clickable trackball,
-directional pad (D-pad) or arrow keys, tab key navigation with an attached keyboard or a software
-application, such as the
+directional controller. Directional controllers can be physical, such as a trackball, directional
+pad (D-pad) or arrow keys, or virtual, such as the
<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a>, that provides an on-screen directional control.</p>
+Eyes-Free Keyboard</a>, or the gestures navigation mode available in Android 4.1 and higher.
+Directional controllers are a primary means of navigation for many Android users.
+</p>
-<p>A directional controller is a primary means of navigation for many users.
-Verify that all user interface (UI) controls in your application are accessible
-without using the touchscreen and that clicking with the center button (or OK button) of a
-directional controller has the same effect as touching the controls on the touchscreen. For
-information on testing directional controls, see <a href="#test-navigation">Testing focus
-navigation</a>.</p>
+<p>To ensure that users can navigate your application using only a directional controller, verify
+that all user interface (UI) input controls in your application can be reached and activated
+without using the touchscreen. You should also verify that clicking with the center button (or OK
+button) of a directional controller has the same effect as touching a control that already has
+focus. For information on testing directional controls, see
+<a href="{@docRoot}tools/testing/testing_accessibility.html#test-navigation">Testing
+focus navigation</a>.</p>
+
<h3 id="focus-enable">Enabling view focus</h3>
-<p>A user interface element is accessible using directional controls when its
+<p>A user interface element is reachable using directional controls when its
<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
{@code android:focusable}</a> attribute is set to {@code true}. This setting allows users to focus
on the element using the directional controls and then interact with it. The user interface controls
@@ -149,44 +171,44 @@
<li>{@link android.view.View#requestFocus requestFocus()}</li>
</ul>
-<p>When working with a view that is not focusable by default, you can make it focusable from the XML
-layout file by setting the
-<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
-{@code android:focusable}</a> attribute to {@code true} or by using the {@link
+<p>If a view is not focusable by default, you can make it focusable in your layout file by setting
+the <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
+{@code android:focusable}</a> attribute to {@code true} or by calling the its {@link
android.view.View#setFocusable setFocusable()} method.</p>
+
<h3 id="focus-order">Controlling focus order</h3>
<p>When users navigate in any direction using directional controls, focus is passed from one
-user interface element (View) to another, as determined by the focus ordering. The ordering of the
-focus movement is based on an algorithm that finds the nearest neighbor in a given direction. In
-rare cases, the default algorithm may not match the order that you intended for your UI. In these
-situations, you can provide explicit overrides to the ordering using the following XML attributes in
-the layout file:</p>
+user interface element (view) to another, as determined by the focus order. This order is based on
+an algorithm that finds the nearest neighbor in a given direction. In rare cases, the algorithm may
+not match the order that you intended or may not be logical for users. In these situations, you can
+provide explicit overrides to the ordering using the following XML attributes in your layout file:
+</p>
<dl>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates down.</dd>
- <a><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft"
->{@code android:nextFocusLeft}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates left.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight"
->{@code android:nextFocusRight}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates right.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates up.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">
+ {@code android:nextFocusDown}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates down.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft">
+ {@code android:nextFocusLeft}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates left.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight">
+ {@code android:nextFocusRight}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates right.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">
+ {@code android:nextFocusUp}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates up.</dd>
</dl>
-<p>The following example XML layout shows two focusable user interface elements where the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a> and <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
+<p>The following example XML layout shows two focusable user interface elements where the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">{@code
+android:nextFocusDown}</a> and
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">{@code
+android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
located to the right of the {@link android.widget.EditText}. However, since these properties have
been set, the {@link android.widget.TextView} element can now be reached by pressing the down arrow
-when focus is on the {@link android.widget.EditText} element: </p>
+when focus is on the {@link android.widget.EditText} element:</p>
<pre>
<LinearLayout android:orientation="horizontal"
@@ -218,8 +240,9 @@
<ul>
<li>Handle directional controller clicks</li>
- <li>Implement Accessibility API methods</li>
- <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom view</li>
+ <li>Implement accessibility API methods</li>
+ <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom
+ view</li>
<li>Populate {@link android.view.accessibility.AccessibilityEvent} and {@link
android.view.accessibility.AccessibilityNodeInfo} for your view</li>
</ul>
@@ -243,9 +266,9 @@
<p>Accessibility events are messages about users interaction with visual interface components in
your application. These messages are handled by <a href="services.html">Accessibility Services</a>,
-which use the information in these events to produce supplemental feedback and prompts when users
-have enabled accessibility services. As of Android 4.0 (API Level 14) and higher, the methods for
-generating accessibility events have been expanded to provide more detailed information beyond the
+which use the information in these events to produce supplemental feedback and prompts. In
+Android 4.0 (API Level 14) and higher, the methods for
+generating accessibility events have been expanded to provide more detailed information than the
{@link android.view.accessibility.AccessibilityEventSource} interface introduced in Android 1.6 (API
Level 4). The expanded accessibility methods are part of the {@link android.view.View} class as well
as the {@link android.view.View.AccessibilityDelegate} class. The methods are as follows:</p>
@@ -262,12 +285,12 @@
<dd>(API Level 4) This method is used when the calling code needs to directly control the check
for accessibility being enabled on the device ({@link
android.view.accessibility.AccessibilityManager#isEnabled AccessibilityManager.isEnabled()}). If
-you do implement this method, you must assume that the calling method has already checked that
-accessibility is enabled and the result is {@code true}. You typically do not need to implement this
-method for a custom view.</dd>
+you do implement this method, you must perform the call as if accessibility is enabled, regardless
+of the actual system setting. You typically do not need to implement this method for a custom view.
+</dd>
<dt>{@link android.view.View#dispatchPopulateAccessibilityEvent
-dispatchPopulateAccessibilityEvent()} </dt>
+dispatchPopulateAccessibilityEvent()}</dt>
<dd>(API Level 4) The system calls this method when your custom view generates an
accessibility event. As of API Level 14, the default implementation of this method calls {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} for this view and
@@ -276,21 +299,21 @@
accessibility services on revisions of Android <em>prior</em> to 4.0 (API Level 14) you
<em>must</em> override this method and populate {@link
android.view.accessibility.AccessibilityEvent#getText} with descriptive text for your custom
-view.</dd>
+view, which is spoken by accessibility services, such as TalkBack.</dd>
<dt>{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()}</dt>
- <dd>(API Level 14) This method sets the text output of an {@link
+ <dd>(API Level 14) This method sets the spoken text prompt of the {@link
android.view.accessibility.AccessibilityEvent} for your view. This method is also called if the
view is a child of a view which generates an accessibility event.
<p class="note"><strong>Note:</strong> Modifying additional attributes beyond the text within
-this method potentially overwrites properties set by other methods. So, while you are able modify
+this method potentially overwrites properties set by other methods. While you can modify
attributes of the accessibility event with this method, you should limit these changes
-to text content only and use the {@link android.view.View#onInitializeAccessibilityEvent
+to text content, and use the {@link android.view.View#onInitializeAccessibilityEvent
onInitializeAccessibilityEvent()} method to modify other properties of the event.</p>
- <p class="note"><strong>Note:</strong> If your implementation of this event calls for completely
-overiding the output text without allowing other parts of your layout to modify its content, then
+ <p class="note"><strong>Note:</strong> If your implementation of this event completely
+overrides the output text without allowing other parts of your layout to modify its content, then
do not call the super implementation of this method in your code.</p>
</dd>
@@ -306,7 +329,7 @@
<dt>{@link android.view.View#onInitializeAccessibilityNodeInfo
onInitializeAccessibilityNodeInfo()}</dt>
<dd>(API Level 14) This method provides accessibility services with information about the state of
-the view. The default {@link android.view.View} implementation sets a standard set of view
+the view. The default {@link android.view.View} implementation has a standard set of view
properties, but if your custom view provides interactive control beyond a simple {@link
android.widget.TextView} or {@link android.widget.Button}, you should override this method and set
the additional information about your view into the {@link
@@ -315,7 +338,7 @@
<dt>{@link android.view.ViewGroup#onRequestSendAccessibilityEvent
onRequestSendAccessibilityEvent()}</dt>
<dd>(API Level 14) The system calls this method when a child of your view has generated an
-{@link android.view.accessibility.AccessibilityEvent}. This step allows the the parent view to amend
+{@link android.view.accessibility.AccessibilityEvent}. This step allows the parent view to amend
the accessibility event with additional information. You should implement this method only if your
custom view can have child views and if the parent view can provide context information to the
accessibility event that would be useful to accessibility services.</dd>
@@ -333,7 +356,7 @@
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()} method to implement the accessibility methods
above. For an example of this approach, see the Android Support Library (revision 5 or higher)
-sample {@code AccessibilityDelegateSupportActivity} in
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/})
</li>
</ul>
@@ -446,20 +469,20 @@
}
</pre>
-<p>On Android 4.0 (API Level 14) and higher, the {@link
+<p>For Android 4.0 (API Level 14) and higher, use the {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} and
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}
-methods are the recommended way to populate or modify the information in an {@link
-android.view.accessibility.AccessibilityEvent}. Use the
+methods to populate or modify the information in an {@link
+android.view.accessibility.AccessibilityEvent}. Use the
{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} method
specifically for adding or modifying the text content of the event, which is turned into audible
prompts by accessibility services such as TalkBack. Use the
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} method for
populating additional information about the event, such as the selection state of the view.</p>
-<p>In addition, you should also implement the
+<p>In addition, implement the
{@link android.view.View#onInitializeAccessibilityNodeInfo onInitializeAccessibilityNodeInfo()}
-method. {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
+method. The {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
are used by accessibility services to investigate the view hierarchy that generated an accessibility
event after receiving that event, to obtain a more detailed context information and provide
appropriate feedback to users.</p>
@@ -467,8 +490,8 @@
<p>The example code below shows how override these three methods by using
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()}. Note that this sample code requires that the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision 5
-or higher) is added to your project.</p>
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision
+5 or higher) is added to your project.</p>
<pre>
ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() {
@@ -509,10 +532,10 @@
}
</pre>
-<p>On applications targeting Android 4.0 (API Level 14) and higher, these methods can be implemented
+<p>In applications targeting Android 4.0 (API Level 14) and higher, you can implement these methods
directly in your custom view class. For another example of this approach, see the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher) sample
-{@code AccessibilityDelegateSupportActivity} in
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher)
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/}).</p>
<p class="note"><strong>Note:</strong> You may find information on implementing accessibility for
@@ -525,50 +548,141 @@
methods.</p>
+<h3 id="virtual-hierarchy">Providing a customized accessibility context</h3>
+
+<p>In Android 4.0 (API Level 14), the framework was enhanced to allow accessibility services to
+ inspect the containing view hierarchy of a user interface component that generates an
+ accessibility event. This enhancement allows accessibility services to provide a much richer set
+ of contextual information with which to aid users.</p>
+
+<p>There are some cases where accessibility services cannot get adequate information
+ from the view hierarchy. An example of this is a custom interface control that has two or more
+ separately clickable areas, such as a calendar control. In this case, the services cannot get
+ adequate information because the clickable subsections are not part of the view hierarchy.</p>
+
+<img src="calendar.png" alt="" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> A custom calendar view with selectable day elements.
+</p>
+
+<p>In the example shown in Figure 1, the entire calendar is implemented as a single view, so if you
+ do not do anything else, accessibility services do not receive enough information about the
+ content of the view and the user's selection within the view. For example, if a user clicks on the
+ day containing <strong>17</strong>, the accessibility framework only receives the description
+ information for the whole calendar control. In this case, the TalkBack accessibility service would
+ simply announce "Calendar" or, only slightly better, "April Calendar" and the user would be left
+ to wonder what day was selected.</p>
+
+<p>To provide adequate context information for accessibility services in situations like this,
+ the framework provides a way to specify a virtual view hierarchy. A <em>virtual view
+ hierarchy</em> is a way for application developers to provide a complementary view hierarchy
+ to accessibility services that more closely matches the actual information on screen. This
+ approach allows accessibility services to provide more useful context information to users.</p>
+
+<p>Another situation where a virtual view hierarchy may be needed is a user interface containing
+ a set of controls (views) that have closely related functions, where an action on one control
+ affects the contents of one or more elements, such as a number picker with separate up and down
+ buttons. In this case, accessibility services cannot get adequate information because action on
+ one control changes content in another and the relationship of those controls may not be apparent
+ to the service. To handle this situation, group the related controls with a containing view and
+ provide a virtual view hierarchy from this container to clearly represent the information and
+ behavior provided by the controls.</p>
+
+<p>In order to provide a virtual view hierarchy for a view, override the {@link
+ android.view.View#getAccessibilityNodeProvider} method in your custom view or view group and
+ return an implementation of {@link android.view.accessibility.AccessibilityNodeProvider}. For an
+ example implementation of this accessibility feature, see
+ {@code AccessibilityNodeProviderActivity} in the ApiDemos sample project. You can implement a
+ virtual view hierarchy that is compatible with Android 1.6 and later by using the
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> with the
+ {@link android.support.v4.view.ViewCompat#getAccessibilityNodeProvider
+ ViewCompat.getAccessibilityNodeProvider()} method and providing an implementation with
+ {@link android.support.v4.view.accessibility.AccessibilityNodeProviderCompat}.</p>
+
+
+<h3 id="custom-touch-events">Handling custom touch events</h3>
+
+<p>Custom view controls may require non-standard touch event behavior. For example, a custom
+control may use the {@link android.view.View#onTouchEvent} listener method to detect the
+{@link android.view.MotionEvent#ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP} events
+and trigger a special click event. In order to maintain compatibility with accessibility services,
+the code that handles this custom click event must do the following:</p>
+
+<ol>
+ <li>Generate an appropriate {@link android.view.accessibility.AccessibilityEvent} for the
+ interpreted click action.</li>
+ <li>Enable accessibility services to perform the custom click action for users who are not able to
+ use a touch screen.</li>
+</ol>
+
+<p>To handle these requirements in an efficient way, your code should override the
+{@link android.view.View#performClick} method, which must call the super implementation of this
+method and then execute whatever actions are required by the click event. When the custom click
+action is detected, that code should then call your {@code performClick()} method. The following
+code example demonstrates this pattern.</p>
+
+<pre>
+class CustomTouchView extends View {
+
+ public CustomTouchView(Context context) {
+ super(context);
+ }
+
+ boolean mDownTouch = false;
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // Listening for the down and up touch events
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownTouch = true;
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ if (mDownTouch) {
+ mDownTouch = false;
+ performClick(); // Call this method to handle the response, and
+ // thereby enable accessibility services to
+ // perform this action for a user who cannot
+ // click the touchscreen.
+ return true;
+ }
+ }
+ return false; // Return false for other touch events
+ }
+
+ @Override
+ public boolean performClick() {
+ // Calls the super implementation, which generates an AccessibilityEvent
+ // and calls the onClick() listener on the view, if any
+ super.performClick();
+
+ // Handle the action for the custom click here
+
+ return true;
+ }
+}
+</pre>
+
+<p>The pattern shown above makes sure that the custom click event is compatible with
+accessibility services by using the {@link android.view.View#performClick} method to both generate
+an accessibility event and provide an entry point for accessibility services to act on behalf of a
+user to perform this custom click event.</p>
+
+<p class="note"><strong>Note:</strong> If your custom view has distinct clickable regions, such as
+a custom calendar view, you must implement a <a href="#virtual-hierarchy">virtual view
+hierarchy</a> by overriding {@link android.view.View#getAccessibilityNodeProvider} in your custom
+view in order to be compatible with accessibility services.</p>
+
+
<h2 id="test">Testing Accessibility</h2>
<p>Testing the accessibility of your application is an important part of ensuring your users have a
-great experience. You can test the most important parts of accessibility by testing your application
-with audible feedback enabled and testing navigation within your application using directional
-controls.</p>
+great experience. You can test the most important accessibility features by using your application
+with audible feedback enabled and navigating within your application using only directional
+controls. For more information on testing accessibility in your application, see the
+<a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a>.
+</p>
-<h3 id="test-audibles">Testing audible feedback</h3>
-<p>You can simulate the experience for many users by enabling an accessibility service that speaks
-as you move around the screen. The Explore by Touch accessibility service, which is available on
-devices with Android 4.0 and later. The <a
-href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a>
-accessibility service, by the <a href="http://code.google.com/p/eyes-free/">Eyes-Free
-Project</a> comes preinstalled on many Android devices.</p>
-
-<p>To enable TalkBack on revisions of Android prior to Android 4.0:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select <strong>Accessibility</strong> to enable it.</li>
- <li>Select <strong>TalkBack</strong> to enable it.</li>
-</ol>
-
-<p class="note"><strong>Note:</strong> If the TalkBack accessibility service is not available, you
-can install it for free from <a href="http://play.google.com">Google Play</a>.</p>
-
-<p>To enable Explore by Touch on Android 4.0 and later:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select the <strong>TalkBack</strong> to enable it.</li>
- <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
-Touch</strong> to enable it.
- <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
-option is not available.</p>
- </li>
-</ol>
-
-<h3 id="test-navigation">Testing focus navigation</h3>
-
-<p>As part of your accessibility testing, you can test navigation of your application using focus,
-even if your test devices does not have a directional controller. The <a
-href="{@docRoot}tools/help/emulator.html">Android Emulator</a> provides a
-simulated directional controller that you can easily use to test navigation. You can also use a
-software-based directional controller, such as the one provided by the
-<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a> to simulate use of a D-pad.</p>
diff --git a/docs/html/guide/topics/ui/accessibility/calendar.png b/docs/html/guide/topics/ui/accessibility/calendar.png
new file mode 100644
index 0000000..ca5c44f
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/calendar.png
Binary files differ
diff --git a/docs/html/guide/topics/ui/accessibility/checklist.jd b/docs/html/guide/topics/ui/accessibility/checklist.jd
new file mode 100644
index 0000000..9473d1b
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/checklist.jd
@@ -0,0 +1,173 @@
+page.title=Accessibility Developer Checklist
+parent.title=Accessibility
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#requirements">Accessibility Requirements</a></li>
+ <li><a href="#recommendations">Accessibility Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Making an application accessible is about a deep commitment to usability, getting the
+details right and delighting your users. This document provides a checklist of accessibility
+requirements, recommendations and considerations to help you make sure your application is
+accessible. Following this checklist does not guarantee your application is accessible, but it's a
+good place to start.</p>
+
+<p>Creating an accessible application is not just the responsibility of developers. Involve your
+design and testing folks as well, and make them are aware of the guidelines for these other stages
+of development:</p>
+
+<ul>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a></li>
+</ul>
+
+<p>In most cases, creating an accessible Android application does not require extensive code
+restructuring. Rather, it means working through the subtle details of how users interact with your
+application, so you can provide them with feedback they can sense and understand. This checklist
+helps you focus on the key development issues to get the details of accessibility right.</p>
+
+
+<h2 id="requirements">Accessibility Requirements</h2>
+
+<p>The following steps must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Describe user interface controls:</strong> Provide content
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">descriptions</a> for user
+ interface components that do not have visible text, particularly
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox} components. Use the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> XML layout attribute or the {@link
+ android.view.View#setContentDescription} method to provide this information for accessibility
+ services. (Exception: <a href="#decorative">decorative graphics</a>)</li>
+ <li><strong>Enable focus-based navigation:</strong> Make sure
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-nav">users can navigate</a>
+ your screen layouts using hardware-based or software directional controls (D-pads, trackballs,
+ keyboards and navigation gestures). In a few cases, you may need to make user interface components
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">focusable</a>
+ or change the <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-order">focus
+ order</a> to be more logical for user actions.</li>
+ <li><strong>Custom view controls:</strong> If you build
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom interface controls</a> for
+ your application, <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">
+ implement accessibility interfaces</a> for your custom views and provide content descriptions.
+ For custom controls that are intended to be compatible with versions of Android back to 1.6,
+ use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> to implement
+ the latest accessibility features.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing. For example, a sound alert
+ for the arrival of a message must be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or other visual alert.</li>
+ <li><strong>Test:</strong> Test accessibility by navigating your application
+ using directional controls, and using eyes-free navigation with TalkBack enabled.
+ For more accessibility testing information, see the
+ <a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a>.</li>
+</ol>
+
+
+<h2 id="recommendations">Accessibility Recommendations</h2>
+
+<p>The following steps are recommended for ensuring the accessibility of your application. If you
+ do not take these actions, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Android Design Accessibility Guidelines:</strong> Before building your layouts,
+ review and follow the accessibility guidelines provided in the
+ <a href="{@docRoot}design/patterns/accessibility.html">Design guidelines</a>.</li>
+ <li><strong>Framework-provided controls:</strong> Use Android's built-in user interface
+ controls whenever possible, as these components provide accessibility support by default.</li>
+ <li><strong>Temporary or self-hiding controls and notifications:</strong> Avoid having user
+ interface controls that fade out or disappear after a certain amount of time. If this behavior
+ is important to your application, provide an alternative interface for these functions.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations where action should be taken to ensure an
+ accessible app. Review this list to see if any of these special cases and considerations apply to
+ your application, and take the appropriate action.</p>
+
+<ol>
+ <li><strong>Text field hints:</strong> For {@link android.widget.EditText} fields, provide an
+ <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
+ attribute <em>instead</em> of a content description, to help users understand what content is
+ expected when the text field is empty and allow the contents of the field to be spoken when
+ it is filled.</li>
+ <li><strong>Custom controls with high visual context:</strong> If your application contains a
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom control</a> with a high degree
+ of visual context (such as a calendar control), default accessibility services processing may
+ not provide adequate descriptions for users, and you should consider providing a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual
+ view hierarchy</a> for your control using
+ {@link android.view.accessibility.AccessibilityNodeProvider}.</li>
+ <li><strong>Custom controls and click handling:</strong> If a custom control in your
+ application performs specific handling of user touch interaction, such as listening with
+ {@link android.view.View#onTouchEvent} for {@link android.view.MotionEvent#ACTION_DOWN
+ MotionEvent.ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP MotionEvent.ACTION_UP}
+ and treating it as a click event, you must trigger an {@link
+ android.view.accessibility.AccessibilityEvent} equivalent to a click and provide a way for
+ accessibility services to perform this action for users. For more information, see
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-touch-events">Handling custom
+ touch events</a>.</li>
+ <li><strong>Controls that change function:</strong> If you have buttons or other controls
+ that change function during the normal activity of a user in your application (for example, a
+ button that changes from <strong>Play</strong> to <strong>Pause</strong>), make sure you also
+ change the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> of the button appropriately.</li>
+ <li><strong>Prompts for related controls:</strong> Make sure sets of controls which provide a
+ single function, such as the {@link android.widget.DatePicker}, provide useful audio feedback
+ when an user interacts with the individual controls.</li>
+ <li><strong>Video playback and captioning:</strong> If your application provides video
+ playback, it must support captioning and subtitles to assist users who are deaf or hard of
+ hearing. Your video playback controls must also clearly indicate if captioning is available for
+ a video and provide a clear way of enabling captions.</li>
+ <li><strong>Supplemental accessibility audio feedback:</strong> Use only the Android accessibility
+ framework to provide accessibility audio feedback for your app. Accessibility services such as
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback"
+ >TalkBack</a> should be the only way your application provides accessibility audio prompts to
+ users. Provide the prompting information with a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> XML layout attribute or dynamically add it using accessibility
+ framework APIs. For example, if your application takes action that you want to announce to a
+ user, such as automatically turning the page of a book, use the {@link
+ android.view.View#announceForAccessibility} method to have accessibility services speak this
+ information to the user.</li>
+ <li><strong>Custom controls with complex visual interactions:</strong> For custom controls that
+ provide complex or non-standard visual interactions, provide a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual view
+ hierarchy</a> for your control using {@link android.view.accessibility.AccessibilityNodeProvider}
+ that allows accessibility services to provide a simplified interaction model for the user. If
+ this approach is not feasible, consider providing an alternate view that is accessible.</li>
+ <li><strong>Sets of small controls:</strong> If you have controls that are smaller than
+ the minimum recommended touch size in your application screens, consider grouping these controls
+ together using a {@link android.view.ViewGroup} and providing a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> for the group.</li>
+ <li id="decorative"><strong>Decorative images and graphics:</strong> Elements in application
+ screens that are purely decorative and do not provide any content or enable a user action should
+ not have accessibility content descriptions.</li>
+</ol>
diff --git a/docs/html/guide/topics/ui/accessibility/index.jd b/docs/html/guide/topics/ui/accessibility/index.jd
index 6fd71e2..6cdbde4 100644
--- a/docs/html/guide/topics/ui/accessibility/index.jd
+++ b/docs/html/guide/topics/ui/accessibility/index.jd
@@ -8,47 +8,67 @@
<h2>Topics</h2>
<ol>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>
- </li>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
- Services</a></li>
+ <li><a href="apps.html">
+ Making Applications Accessible</a></li>
+ <li><a href="checklist.html">
+ Accessibility Developer Checklist</a></li>
+ <li><a href="services.html">
+ Building Accessibility Services</a></li>
</ol>
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.view.accessibility.AccessibilityEvent}</li>
- <li>{@link android.accessibilityservice.AccessibilityService}</li>
- </ol>
-
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ </ol>
+
+ <h2>Related Videos</h2>
+ <ol>
+ <li>
+ <iframe title="Google I/O 2012 - Making Android Apps Accessible"
+ width="210" height="160"
+ src="http://www.youtube.com/embed/q3HliaMjL38?rel=0&hd=1"
+ frameborder="0" allowfullscreen>
+ </iframe>
+ <li>
</ol>
</div>
</div>
-<p>Many Android users have disabilities that require them to interact with their Android devices in
-different ways. These include users who have visual, physical or age-related disabilities that
-prevent them from fully seeing or using a touchscreen.</p>
+<p>Many Android users have different abilities that require them to interact with their Android
+devices in different ways. These include users who have visual, physical or age-related limitations
+that prevent them from fully seeing or using a touchscreen, and users with hearing loss who may not
+be able to perceive audible information and alerts.</p>
<p>Android provides accessibility features and services for helping these users navigate their
-devices more easily, including text-to-speech, haptic feedback, trackball and D-pad navigation that
-augment their experience. Android application developers can take advantage of these services to
-make their applications more accessible and also build their own accessibility services.</p>
+devices more easily, including text-to-speech, haptic feedback, gesture navigation, trackball and
+directional-pad navigation. Android application developers can take advantage of these services to
+make their applications more accessible.</p>
+
+<p>Android developers can also build their own accessibility services, which can provide
+enhanced usability features such as audio prompting, physical feedback, and alternative navigation
+modes. Accessibility services can provide these enhancements for all applications, a set of
+applications or just a single app.</p>
<p>The following topics show you how to use the Android framework to make applications more
accessible.</p>
<dl>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications
-Accessible</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a></strong>
</dt>
<dd>Development practices and API features to ensure your application is accessible to users with
disabilities.</dd>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
-Services</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a></strong>
+ </dt>
+ <dd>A checklist to help developers ensure that their applications are accessible.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">
+ Building Accessibility Services</a></strong>
</dt>
<dd>How to use API features to build services that make other applications more accessible for
users.</dd>
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
index 7d36181..2a6fe7a 100644
--- a/docs/html/guide/topics/ui/accessibility/services.jd
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -14,8 +14,16 @@
<li><a href="#service-config">Accessibility service configuration</a></li>
</ol>
</li>
+ <li><a href="#register">Registering for Accessibility Events</a></li>
<li><a href="#methods">AccessibilityService Methods</a></li>
<li><a href="#event-details">Getting Event Details</a></li>
+ <li><a href="#act-for-users">Taking Action for Users</a>
+ <ol>
+ <li><a href="#detect-gestures">Listening for gestures</a></li>
+ <li><a href="#using-actions">Using accessibility actions</a></li>
+ <li><a href="#focus-types">Using focus types</a></li>
+ </ol>
+ </li>
<li><a href="#examples">Example Code</a></li>
</ol>
@@ -30,7 +38,7 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
@@ -45,20 +53,20 @@
create and distribute their own services. This document explains the basics of building an
accessibility service.</p>
-<p>The ability for you to build and deploy accessibility services was introduced with Android
-1.6 (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
-Support Library was also updated with the release of Android 4.0 to provide support for these
-enhanced accessibility features back to Android 1.6. Developers aiming for widely compatible
-accessibility services are encouraged to use the
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> and develop for the more
-advanced accessibility features introduced in Android 4.0.</p>
+<p>The ability for you to build and deploy accessibility services was introduced with Android 1.6
+ (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> was also updated with
+ the release of Android 4.0 to provide support for these enhanced accessibility features back to
+ Android 1.6. Developers aiming for widely compatible accessibility services are encouraged to use
+ the Support Library and develop for the more advanced accessibility features introduced in
+ Android 4.0.</p>
<h2 id="manifest">Manifest Declarations and Permissions</h2>
<p>Applications that provide accessibility services must include specific declarations in their
- application manifests in order to be treated as an accessibility service by an Android system.
- This section explains the required and optional settings for accessibility services.</p>
+ application manifests to be treated as an accessibility service by the Android system. This
+ section explains the required and optional settings for accessibility services.</p>
<h3 id="service-declaration">Accessibility service declaration</h3>
@@ -66,7 +74,9 @@
<p>In order to be treated as an accessibility service, your application must include the
{@code service} element (rather than the {@code activity} element) within the {@code application}
element in its manifest. In addition, within the {@code service} element, you must also include an
-accessibility service intent filter, as shown in the following sample:</p>
+accessibility service intent filter. For compatiblity with Android 4.1 and higher, the manifest
+must also request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission
+as shown in the following sample:</p>
<pre>
<application>
@@ -76,6 +86,7 @@
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
+ <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
</application>
</pre>
@@ -123,27 +134,6 @@
/>
</pre>
-<p>One of the most important functions of the accessibility service configuration parameters is to
-allow you to specify what types of accessibility events your service can handle. Being able to
-specify this information enables accessibility services to cooperate with each other, and allows you
-as a developer the flexibility to handle only specific events types from specific applications. The
-event filtering can include the following criteria:</p>
-
-<ul>
- <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
-events you want your service to handle. If this parameter is omitted, your accessibility service is
-considered available to service accessibility events for any application. This parameter can be set
-in the accessibility service configuration files with the {@code android:packageNames} attribute as
-a comma-separated list, or set using the {@link
-android.accessibilityservice.AccessibilityServiceInfo#packageNames
-AccessibilityServiceInfo.packageNames} member.</li>
- <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
-to handle. This parameter can be set in the accessibility service configuration files with the
-{@code android:accessibilityEventTypes} attribute as a comma-separated list, or set using the
-{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
-AccessibilityServiceInfo.eventTypes} member. </li>
-</ul>
-
<p>For more information about the XML attributes which can be used in the accessibility service
configuration file, follow these links to the reference documentation:</p>
@@ -162,9 +152,45 @@
the {@link android.accessibilityservice.AccessibilityServiceInfo} reference documentation.</p>
+<h2 id="register">Registering for Accessibility Events</h2>
+
+<p>One of the most important functions of the accessibility service configuration parameters is to
+allow you to specify what types of accessibility events your service can handle. Being able to
+specify this information enables accessibility services to cooperate with each other, and allows you
+as a developer the flexibility to handle only specific events types from specific applications. The
+event filtering can include the following criteria:</p>
+
+<ul>
+ <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
+events you want your service to handle. If this parameter is omitted, your accessibility service is
+considered available to service accessibility events for any application. This parameter can be set
+in the accessibility service configuration files with the {@code android:packageNames} attribute as
+a comma-separated list, or set using the {@link
+android.accessibilityservice.AccessibilityServiceInfo#packageNames
+AccessibilityServiceInfo.packageNames} member.</li>
+ <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
+to handle. This parameter can be set in the accessibility service configuration files with the
+{@code android:accessibilityEventTypes} attribute as a list separated by the {@code |} character
+(for example {@code accessibilityEventTypes="typeViewClicked|typeViewFocused"}), or set using the
+{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
+AccessibilityServiceInfo.eventTypes} member. </li>
+</ul>
+
+<p>When setting up your accessibility service, carefully consider what events your service is able
+to handle and only register for those events. Since users can activate more than one accessibility
+services at a time, your service must not consume events that it is not able to handle. Remember
+that other services may handle those events in order to improve a user's experience.</p>
+
+<p class="note"><strong>Note:</strong> The Android framework dispatches accessibility events to
+more than one accessibility service if the services provide different
+<a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityFeedbackType">
+feedback types</a>. However, if two or more services provide the same feedback type, then only the
+first registered service receives the event.</p>
+
+
<h2 id="methods">AccessibilityService Methods</h2>
-<p>An application that provides accessibility service must extend the {@link
+<p>An accessibility service must extend the {@link
android.accessibilityservice.AccessibilityService} class and override the following methods from
that class. These methods are presented in the order in which they are called by the Android system,
from when the service is started
@@ -188,15 +214,15 @@
{@link android.view.accessibility.AccessibilityEvent} that matches the event filtering parameters
specified by your accessibility service. For example, when the user clicks a button or focuses on a
user interface control in an application for which your accessibility service is providing feedback.
-When this happens, the system calls this method of your service with the associated {@link
-android.view.accessibility.AccessibilityEvent}, which you can then interpret and provide feedback to
-the user. This method may be called many times over the lifecycle of your service.</li>
+When this happens, the system calls this method, passing the associated {@link
+android.view.accessibility.AccessibilityEvent}, which the service can then interpret and use to
+provide feedback to the user. This method may be called many times over the lifecycle of your
+service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onInterrupt onInterrupt()} -
(required) This method is called when the system wants to interrupt the feedback your service is
-providing, usually in response to a user taking action, such as moving focus to a different user
-interface control than the one for which you are currently providing feedback. This method may be
-called many times over the lifecycle of your service.</li>
+providing, usually in response to a user action such as moving focus to a different control. This
+method may be called many times over the lifecycle of your service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onUnbind onUnbind()} - (optional)
This method is called when the system is about to shutdown the accessibility service. Use this
@@ -206,7 +232,9 @@
<p>These callback methods provide the basic structure for your accessibility service. It is up to
you to decide on how to process data provided by the Android system in the form of {@link
-android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user.</p>
+android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user. For more
+information about getting information from an accessibility event, see the
+<a href="{@docRoot}training/accessibility/service.html">Implementing Accessibility</a> training.</p>
<h2 id="event-details">Getting Event Details</h2>
@@ -214,15 +242,15 @@
<p>The Android system provides information to accessibility services about the user interface
interaction through {@link android.view.accessibility.AccessibilityEvent} objects. Prior to Android
4.0, the information available in an accessibility event, while providing a significant amount of
-detail about a user interface control selected by the user, typically provided limited contextual
+detail about a user interface control selected by the user, offered limited contextual
information. In many cases, this missing context information might be critical to understanding the
meaning of the selected control.</p>
-<p>A typical example of an interface where context is of critical importance is a calendar or day
-planner. If a user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility
-service announces “4 PM”, but fails to indicate this is a Friday a Monday, the month or day, this is
-hardly ideal feedback for the user. In this case, the context of a user interface control is of
-critical importance to a user who wants to schedule a meeting.</p>
+<p>An example of an interface where context is critical is a calendar or day planner. If the
+user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility service
+announces “4 PM”, but does not announce the weekday name, the day of the month, or the month name,
+the resulting feedback is confusing. In this case, the context of a user interface control is
+critical to a user who wants to schedule a meeting.</p>
<p>Android 4.0 significantly extends the amount of information that an accessibility service can
obtain about an user interface interaction by composing accessibility events based on the view
@@ -245,26 +273,167 @@
AccessibilityEvent.getRecordCount()} and {@link
android.view.accessibility.AccessibilityEvent#getRecord getRecord(int)} - These methods allow you to
retrieve the set of {@link android.view.accessibility.AccessibilityRecord} objects which contributed
-to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system, which can
-provide more context for your accessibility service.</li>
+to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system. This level
+of detail provides more context for the event that triggered your accessibility service.</li>
<li>{@link android.view.accessibility.AccessibilityEvent#getSource
AccessibilityEvent.getSource()} - This method returns an {@link
-android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request the
-parents and children of the component that originated the accessibility event and investigate their
-contents and state in order to provide
+android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request view
+layout hierarchy (parents and children) of the component that originated the accessibility event.
+This feature allows an accessibility service to investigate the full context of an event, including
+the content and state of any enclosing views or child views.
- <p class="caution"><strong>Important:</strong> The ability to investigate the full view
+<p class="caution"><strong>Important:</strong> The ability to investigate the view
hierarchy from an {@link android.view.accessibility.AccessibilityEvent} potentially exposes private
user information to your accessibility service. For this reason, your service must request this
level of access through the accessibility <a href="#service-config">service configuration XML</a>
file, by including the {@code canRetrieveWindowContent} attribute and setting it to {@code true}. If
you do not include this setting in your service configuration xml file, calls to {@link
android.view.accessibility.AccessibilityEvent#getSource getSource()} fail.</p>
+
+<p class="note"><strong>Note:</strong> In Android 4.1 (API Level 16) and higher, the
+{@link android.view.accessibility.AccessibilityEvent#getSource getSource()} method,
+as well as {@link android.view.accessibility.AccessibilityNodeInfo#getChild
+AccessibilityNodeInfo.getChild()} and
+{@link android.view.accessibility.AccessibilityNodeInfo#getParent getParent()}, return only
+view objects that are considered important for accessibility (views that draw content or respond to
+user actions). If your service requires all views, it can request them by setting the
+{@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service's
+{@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+{@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS}.</p>
</li>
</ul>
+<h2 id="act-for-users">Taking Action for Users</h2>
+
+<p>Starting with Android 4.0 (API Level 14), accessibility services can act on behalf
+ of users, including changing the input focus and selecting (activating) user interface elements.
+ In Android 4.1 (API Level 16) the range of actions has been expanded to include scrolling lists
+ and interacting with text fields. Accessibility services can
+ also take global actions, such as navigating to the Home screen, pressing the Back button, opening
+ the notifications screen and recent applications list. Android 4.1 also includes a new type of
+ focus, <em>Accessibilty Focus</em>, which makes all visible elements selectable by an
+ accessibility service.</p>
+
+<p>These new capabilities make it possible for developers of accessibility services to create
+ alternative navigation modes such as
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">gesture navigation</a>,
+ and give users with disabilities improved control of their Android devices.</p>
+
+
+<h3 id="detect-gestures">Listening for gestures</h3>
+
+<p>Accessibility services can listen for specific gestures and respond by taking action on behalf
+ of a user. This feature, added in Android 4.1 (API Level 16), and requires that your
+ accessibility service request activation of the Explore by Touch feature. Your service can
+ request this activation by setting the
+ {@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service’s
+ {@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+ {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE},
+ as shown in the following example.
+ </p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+ @Override
+ public void onCreate() {
+ getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+ }
+ ...
+}
+</pre>
+
+<p>Once your service has requested activation of Explore by Touch, the user must allow the
+ feature to be turned on, if it is not already active. When this feature is active, your service
+ receives notification of accessibility gestures through your service's
+ {@link android.accessibilityservice.AccessibilityService#onGesture onGesture()} callback method
+ and can respond by taking actions for the user.</p>
+
+
+<h3 id="using-actions">Using accessibility actions</h3>
+
+<p>Accessibility services can take action on behalf of users to make interacting with applications
+ simpler and more productive. The ability of accessibility services to perform actions was added
+ in Android 4.0 (API Level 14) and significantly expanded with Android 4.1 (API Level 16).</p>
+
+<p>In order to take actions on behalf of users, your accessibility service must
+ <a href="#register">register</a> to receive events from a few or many applications and request
+ permission to view the content of applications by setting the
+ <a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_canRetrieveWindowContent">
+ {@code android:canRetrieveWindowContent}</a> to {@code true} in the
+ <a href="#service-config">service configuration file</a>. When events are received by your
+ service, it can then retrieve the
+ {@link android.view.accessibility.AccessibilityNodeInfo} object from the event using
+ {@link android.view.accessibility.AccessibilityEvent#getSource getSource()}.
+ With the {@link android.view.accessibility.AccessibilityNodeInfo} object, your service can then
+ explore the view hierarchy to determine what action to take and then act for the user using
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()}.</p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ // get the source node of the event
+ AccessibilityNodeInfo nodeInfo = event.getSource();
+
+ // Use the event and node information to determine
+ // what action to take
+
+ // take action on behalf of the user
+ nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+
+ // recycle the nodeInfo object
+ nodeInfo.recycle();
+ }
+ ...
+}
+</pre>
+
+<p>The {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()} method
+ allows your service to take action within an application. If your service needs to perform a
+ global action such as navigating to the Home screen, pressing the Back button, opening the
+ notifications screen or recent applications list, then use the
+ {@link android.accessibilityservice.AccessibilityService#performGlobalAction performGlobalAction()}
+ method.</p>
+
+
+<h3 id="focus-types">Using focus types</h3>
+
+<p>Android 4.1 (API Level 16) introduces a new type of user interface focus called <em>Accessibility
+ Focus</em>. This type of focus can be used by accessibility services to select any visible user
+ interface element and act on it. This focus type is different from the more well known <em>Input
+ Focus</em>, which determines what on-screen user interface element receives input when a user
+ types characters, presses <strong>Enter</strong> on a keyboard or pushes the center button of a
+ D-pad control.</p>
+
+<p>Accessibility Focus is completely separate and independent from Input Focus. In fact, it is
+ possible for one element in a user interface to have Input Focus while another element has
+ Accessibility Focus. The purpose of Accessibility Focus is to provide accessibility services with
+ a method of interacting with any visible element on a screen, regardless of whether or not the
+ element is input-focusable from a system perspective. You can see accessibility focus in action by
+ testing accessibility gestures. For more information about testing this feature, see
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">Testing gesture
+ navigation</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services that use Accessibility Focus are responsible for
+ synchronizing the current Input Focus when an element is capable of this type of focus. Services
+ that do not synchronize Input Focus with Accessibility Focus run the risk of causing problems in
+ applications that expect input focus to be in a specific location when certain actions are taken.
+ </p>
+
+<p>An accessibility service can determine what user interface element has Input Focus or
+ Accessibility Focus using the {@link android.view.accessibility.AccessibilityNodeInfo#findFocus
+ AccessibilityNodeInfo.findFocus()} method. You can also search for elements that can be selected
+ with Input Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#focusSearch focusSearch()} method.
+ Finally, your accessibility service can set Accessibility Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction
+ performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS)} method.</p>
+
+
<h2 id="examples">Example Code</h2>
<p>The API Demo project contains two samples which can be used as a starting point for generating
diff --git a/docs/html/tools/testing/testing_accessibility.jd b/docs/html/tools/testing/testing_accessibility.jd
new file mode 100644
index 0000000..daf9b36
--- /dev/null
+++ b/docs/html/tools/testing/testing_accessibility.jd
@@ -0,0 +1,257 @@
+page.title=Accessibility Testing Checklist
+parent.title=Testing
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#goals">Testing Goals</a></li>
+ <li><a href="#requirements">Testing Requirements</a></li>
+ <li><a href="#recommendations">Testing Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ <li><a href="#how-to">Testing Accessibility Features</a>
+ <ol>
+ <li><a href="#test-audibles">Testing audible feedback</a></li>
+ <li><a href="#test-navigation">Testing focus navigation</a></li>
+ <li><a href="#test-gestures">Testing gesture navigation</a></li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a>
+ </li>
+ <li>
+ <a href="{@docRoot}design/patterns/accessibility.html">
+ Android Design: Accessibility</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Testing is an important part of making your application accessible to users with varying
+ abilities. Following <a href="{@docRoot}design/patterns/accessibility.html">design</a> and
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">development</a> guidelines for
+ accessibility are important steps toward that goal, but testing for accessibility can uncover
+ problems with user interaction that are not obvious during design and development.</p>
+
+<p>This accessibility testing checklist guides you through the important aspects of
+ accessibility testing, including overall goals, required testing steps, recommended testing and
+ special considerations. This document also discusses how to enable accessibility features on
+ Android devices for testing purposes.</p>
+
+
+<h2 id="goals">Testing Goals</h2>
+
+<p>Your accessibility testing should have the following, high level goals:</p>
+
+<ul>
+ <li>Set up and use the application without sighted assistance</li>
+ <li>All task workflows in the application can be easily navigated using directional controls and
+ provide clear and appropriate feedback</li>
+</ul>
+
+
+<h2 id="requirements">Testing Requirements</h2>
+
+<p>The following tests must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Directional controls:</strong> Verify that the application can be operated
+ without the use of a touch screen. Attempt to use only directional controls to accomplish the
+ primary tasks in the application. Use the keyboard and directional-pad (D-Pad) controls in the
+ Android <a href="{@docRoot}tools/devices/emulator.html">Emulator</a> or use
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gesture
+ navigation</a> on devices with Android 4.1 (API Level 16) or higher.
+ <p class="note"><strong>Note:</strong> Keyboards and D-pads provide different navigation paths
+ than accessibility gestures. While gestures allow users to focus on nearly any on-screen
+ content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+ </li>
+ <li><strong>TalkBack audio prompts:</strong> Verify that user interface controls that provide
+ information (graphics or text) or allow user action have clear and accurate audio descriptions
+ when <a href="#testing-talkback">TalkBack is enabled</a> and controls are focused. Use
+ directional controls to move focus between application layout elements.</li>
+ <li><strong>Explore by Touch prompts:</strong> Verify that user interface controls that
+ provide information (graphics or text) or allow user action have appropriate audio descriptions
+ when <a href="#testing-ebt">Explore by Touch is enabled</a>. There should be no
+ regions where contents or controls do not provide an audio description.</li>
+ <li><strong>Touchable control sizes:</strong> All controls where a user can select or take an
+ action must be a minimum of 48 dp (approximately 9mm) in length and width, as recommended by
+ <a href="{@docRoot}design/patterns/accessibility.html">Android Design</a>.</li>
+ <li><strong>Gestures work with TalkBack enabled:</strong> Verify that app-specific gestures,
+ such as zooming images, scrolling lists, swiping between pages or navigating carousel controls
+ continue to work when <a href="#testing-talkback">TalkBack is enabled</a>. If these gestures do
+ not function, then an alternative interface for these actions must be provided.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing, for example: A sound alert
+ for the arrival of a message should also be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or another visual alert.</li>
+</ol>
+
+
+<h2 id="recommendations">Testing Recommendations</h2>
+
+<p>The following tests are recommended for ensuring the accessibility of your application. If you
+ do not test these items, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Repetitive audio prompting:</strong> Check that closely related controls (such as
+ items with multiple components in a list) do not simply repeat the same audio prompt. For
+ example, in a contacts list that contains a contact picture, written name and title, the prompts
+ should not simply repeat “Bob Smith” for each item.</li>
+ <li><strong>Audio prompt overloading or underloading:</strong> Check that closely related
+ controls provide an appropriate level of audio information that enables users to understand and
+ act on a screen element. Too little or too much prompting can make it difficult to understand
+ and use a control.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations that should be tested to ensure an
+ accessible app. Some, none or all of the cases described here may apply to your application. Be
+ sure to review this list to find out if these special cases apply and take appropriate action.</p>
+
+<ol>
+ <li><strong>Review developer special cases and considerations:</strong> Review the list of
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html#special-cases">special cases</a>
+ for accessibility development and test your application for the cases that apply.</li>
+ <li><strong>Prompts for controls that change function:</strong> Buttons or other controls
+ that change function due to application context or workflow must provide audio prompts
+ appropriate to their current function. For example, a button that changes function from play
+ video to pause video should provide an audio prompt which is appropriate to its current state.</li>
+ <li><strong>Video playback and captioning:</strong> If the application provides video
+ playback, verify that it supports captioning and subtitles to assist users who are deaf or hard
+ of hearing. The video playback controls must clearly indicate if captioning is available for a
+ video and provide a clear way of enabling captions.</li>
+</ol>
+
+
+<h2 id="how-to">Testing Accessibility Features</h2>
+
+<p>Testing of accessibility features such as TalkBack, Explore by Touch and accessibility Gestures
+requires setup of your testing device. This section describes how to enable these features for
+accessibility testing.</p>
+
+
+<h3 id="test-audibles">Testing audible feedback</h3>
+
+<p>Audible accessibility feedback features on Android devices provide audio prompts that speaks
+ the screen content as you move around an application. By enabling these features on an Android
+ device, you can test the experience of users with blindness or low-vision using your application.
+</p>
+
+<p>Audible feedback for users on Android is typically provided by TalkBack accessibility service and
+the Explore by Touch system feature. The TalkBack accessibility service comes preinstalled on most
+Android devices and can also be downloaded for free from
+<a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">Google
+Play</a>. The Explore by Touch system feature is available on devices running Android 4.0 and later.
+</p>
+
+<h4 id="testing-talkback">Testing with TalkBack</h4>
+
+<p>The <em>TalkBack</em> accessibility service works by speaking the contents of user interface
+controls as the user moves focus onto controls. This service should be enabled as part of testing
+focus navigation and audible prompts.</p>
+
+<p>To enable the TalkBack accessibility service:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select <strong>Accessibility</strong> to enable it.</li>
+ <li>Select <strong>TalkBack</strong> to enable it.</li>
+</ol>
+
+<p class="note">
+ <strong>Note:</strong> While TalkBack is the most available Android accessibility service for
+ users with disabilities, other accessibility services are available and may be installed by users.
+</p>
+
+<p>For more information about using TalkBack, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700928">Use TalkBack</a>.</p>
+
+<h4 id="testing-ebt">Testing with Explore by Touch</h4>
+
+<p>The <em>Explore by Touch</em> system feature is available on devices running Android 4.0 and
+ later, and works by enabling a special accessibility mode that allows users to drag a finger
+ around the interface of an application and hear the contents of the screen spoken. This feature
+ does not require screen elements to be focused using an directional controller, but listens for
+ hover events over user interface controls.
+</p>
+
+<p>To enable Explore by Touch on Android 4.0 and later:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select the <strong>TalkBack</strong> to enable it.
+ <p class="note"><strong>Note:</strong> On Android 4.1 (API Level 16) and higher, the system
+ provides a popup message to enable Explore by Touch. On older versions, you must follow the
+ step below.</p>
+ </li>
+ <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
+Touch</strong> to enable it.
+ <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
+option is not available.</p>
+ </li>
+</ol>
+
+<p>For more information about using the Explore by Touch features, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700722">Use Explore by
+Touch</a>.</p>
+
+<h3 id="test-navigation">Testing focus navigation</h3>
+
+<p>Focus navigation is the use of directional controls to navigate between the individual user
+ interface elements of an application in order to operate it. Users with limited vision or limited
+ manual dexterity often use this mode of navigation instead of touch navigation. As part of
+ accessibility testing, you should verify that your application can be operated using only
+ directional controls.</p>
+
+<p>You can test navigation of your application using only focus controls, even if your test devices
+ does not have a directional controller. The <a href="{@docRoot}tools/help/emulator.html">Android
+ Emulator</a> provides a simulated directional controller that you can use to test navigation. You
+ can also use a software-based directional controller, such as the one provided by the
+ <a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin"
+ >Eyes-Free Keyboard</a> to simulate use of a D-pad on a test device that does not have a physical
+ D-pad.</p>
+
+
+<h3 id="test-gestures">Testing gesture navigation</h3>
+
+<p>Gesture navigation is an accessibility navigation mode that allows users to navigate Android
+ devices and applications using specific
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gestures</a>. This
+ navigation mode is available on Android 4.1 (API Level 16) and higher.</p>
+
+<p class="note"><strong>Note:</strong> Accessibility gestures provide a different navigation path
+than keyboards and D-pads. While gestures allow users to focus on nearly any on-screen
+content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+
+<p>To enable gesture navigation on Android 4.1 and later:</p>
+<ul>
+ <li>Enable both TalkBack and the Explore by Touch feature as described in the
+ <a href="#testing-ebt">Testing with Explore by Touch</a>. When <em>both</em> of these
+ features are enabled, accessibility gestures are automatically enabled.</li>
+ <li>You can change gesture settings using <strong>Settings > Accessibility > TalkBack >
+ Settings > Manage shortcut gestures</strong>.
+</ul>
+
+<p>For more information about using Explore by Touch accessibility gestures, see
+<a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">Accessibility
+gestures</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services other than TalkBack may map accessibility gestures
+ to different user actions. If gestures are not producing the expected actions during testing, try
+ disabling other accessibility services before proceeding.</p>
\ No newline at end of file
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 850e0ec..f3936b8 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -5,7 +5,7 @@
<a href="<?cs var:toroot ?>tools/index.html"><span class="en">Developer Tools</span></a>
</div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>sdk/index.html"><span class="en">Download</span></a></div>
@@ -29,7 +29,7 @@
</li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="/tools/workflow/index.html"><span class="en">Workflow</span></a>
@@ -39,8 +39,8 @@
<div class="nav-section-header"><a href="/tools/devices/index.html"><span class="en">Setting Up Virtual Devices</span></a></div>
<ul>
<li><a href="/tools/devices/managing-avds.html"><span class="en">With AVD Manager</span></a></li>
- <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
- <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
+ <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
</ul>
</li>
<li><a href="/tools/device.html"><span class="en">Using Hardware Devices</span></a></li>
@@ -48,16 +48,16 @@
<div class="nav-section-header"><a href="/tools/projects/index.html"><span class="en">Setting Up Projects</span></a></div>
<ul>
<li><a href="/tools/projects/projects-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="/tools/building/index.html"><span class="en">Building and Running</span></a></div>
<ul>
<li><a href="/tools/building/building-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
@@ -76,7 +76,7 @@
</li>
<li><a href="<?cs var:toroot ?>tools/testing/testing_otheride.html">
<span class="en">From Other IDEs</span></a>
- </li>
+ </li>
<li>
<a href="<?cs var:toroot?>tools/testing/activity_testing.html">
<span class="en">Activity Testing</span></a>
@@ -90,6 +90,10 @@
<span class="en">Content Provider Testing</span></a>
</li>
<li>
+ <a href="<?cs var:toroot?>tools/testing/testing_accessibility.html">
+ <span class="en">Accessibility Testing</span></a>
+ </li>
+ <li>
<a href="<?cs var:toroot ?>tools/testing/what_to_test.html">
<span class="en">What To Test</span></a>
</li>
@@ -160,7 +164,7 @@
<li><a href="<?cs var:toroot ?>tools/help/zipalign.html">zipalign</a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/revisions/index.html"><span class="en">Revisions</span></a></div>
@@ -178,8 +182,8 @@
class="en">Platforms</span></a></li>
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/extras/index.html"><span class="en">Extras</span></a></div>
@@ -192,13 +196,13 @@
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot
?>tools/samples/index.html"><span class="en">Samples</span></a></div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>tools/adk/index.html">
@@ -217,7 +221,7 @@
</li>
</ul>
</li>
-
+
</ul><!-- nav -->
<script type="text/javascript">
diff --git a/docs/html/training/basics/activity-lifecycle/recreating.jd b/docs/html/training/basics/activity-lifecycle/recreating.jd
index 3bbf0bb..8c7126a 100644
--- a/docs/html/training/basics/activity-lifecycle/recreating.jd
+++ b/docs/html/training/basics/activity-lifecycle/recreating.jd
@@ -51,7 +51,7 @@
the foreground activity because the screen configuration has changed and your activity might need to
load alternative resources (such as the layout).</p>
-<p>By default, the system uses the {@link android.os.Bundle} instance state to saves information
+<p>By default, the system uses the {@link android.os.Bundle} instance state to save information
about each {@link android.view.View} object in your activity layout (such as the text value entered
into an {@link android.widget.EditText} object). So, if your activity instance is destroyed and
recreated, the state of the layout is automatically restored to its previous state. However, your
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e82ccd4..7a4a1ca 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -80,6 +80,7 @@
@Deprecated
public BitmapDrawable() {
mBitmapState = new BitmapState((Bitmap) null);
+ mMutated = true;
}
/**
@@ -90,6 +91,7 @@
public BitmapDrawable(Resources res) {
mBitmapState = new BitmapState((Bitmap) null);
mBitmapState.mTargetDensity = mTargetDensity;
+ mMutated = true;
}
/**
@@ -100,6 +102,7 @@
@Deprecated
public BitmapDrawable(Bitmap bitmap) {
this(new BitmapState(bitmap), null);
+ mMutated = true;
}
/**
@@ -109,6 +112,7 @@
public BitmapDrawable(Resources res, Bitmap bitmap) {
this(new BitmapState(bitmap), res);
mBitmapState.mTargetDensity = mTargetDensity;
+ mMutated = true;
}
/**
@@ -122,6 +126,7 @@
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
}
+ mMutated = true;
}
/**
@@ -134,6 +139,7 @@
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
}
+ mMutated = true;
}
/**
@@ -147,6 +153,7 @@
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
}
+ mMutated = true;
}
/**
@@ -159,6 +166,7 @@
if (mBitmap == null) {
android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
}
+ mMutated = true;
}
/**
@@ -552,6 +560,7 @@
} else {
mTargetDensity = state.mTargetDensity;
}
+ mMutated = false;
setBitmap(state != null ? state.mBitmap : null);
}
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index bade9b4..723db6e 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -239,7 +239,11 @@
return null;
}
-
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ mClipState.mDrawable.setLayoutDirection(layoutDirection);
+ super.setLayoutDirection(layoutDirection);
+ }
final static class ClipState extends ConstantState {
Drawable mDrawable;
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 88c9155..f8e3944 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -36,12 +36,14 @@
public class ColorDrawable extends Drawable {
private ColorState mState;
private final Paint mPaint = new Paint();
+ private boolean mMutated;
/**
* Creates a new black ColorDrawable.
*/
public ColorDrawable() {
this(null);
+ mMutated = true;
}
/**
@@ -52,6 +54,7 @@
public ColorDrawable(int color) {
this(null);
setColor(color);
+ mMutated = true;
}
private ColorDrawable(ColorState state) {
@@ -63,6 +66,21 @@
return super.getChangingConfigurations() | mState.mChangingConfigurations;
}
+ /**
+ * A mutable BitmapDrawable still shares its Bitmap with any other Drawable
+ * that comes from the same resource.
+ *
+ * @return This drawable.
+ */
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mState = new ColorState(mState);
+ mMutated = true;
+ }
+ return this;
+ }
+
@Override
public void draw(Canvas canvas) {
if ((mState.mUseColor >>> 24) != 0) {
@@ -165,6 +183,7 @@
if (state != null) {
mBaseColor = state.mBaseColor;
mUseColor = state.mUseColor;
+ mChangingConfigurations = state.mChangingConfigurations;
}
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 4bc5a5a..020a54f 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -376,8 +376,8 @@
/**
* Returns the resolved layout direction for this Drawable.
*
- * @return One of {@link View#LAYOUT_DIRECTION_LTR},
- * {@link View#LAYOUT_DIRECTION_RTL}
+ * @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
+ * {@link android.view.View#LAYOUT_DIRECTION_RTL}
*/
public int getLayoutDirection() {
return mLayoutDirection;
@@ -387,8 +387,8 @@
* Set the layout direction for this drawable. Should be a resolved direction as the
* Drawable as no capacity to do the resolution on his own.
*
- * @param layoutDirection One of {@link View#LAYOUT_DIRECTION_LTR},
- * {@link View#LAYOUT_DIRECTION_RTL},
+ * @param layoutDirection One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
+ * {@link android.view.View#LAYOUT_DIRECTION_RTL},
*
*/
public void setLayoutDirection(int layoutDirection) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 15b2c0b..486390c 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -105,7 +105,7 @@
mAlpha = alpha;
if (mCurrDrawable != null) {
if (mEnterAnimationEnd == 0) {
- mCurrDrawable.setAlpha(alpha);
+ mCurrDrawable.mutate().setAlpha(alpha);
} else {
animate(false);
}
@@ -118,7 +118,7 @@
if (mDrawableContainerState.mDither != dither) {
mDrawableContainerState.mDither = dither;
if (mCurrDrawable != null) {
- mCurrDrawable.setDither(mDrawableContainerState.mDither);
+ mCurrDrawable.mutate().setDither(mDrawableContainerState.mDither);
}
}
}
@@ -128,7 +128,7 @@
if (mColorFilter != cf) {
mColorFilter = cf;
if (mCurrDrawable != null) {
- mCurrDrawable.setColorFilter(cf);
+ mCurrDrawable.mutate().setColorFilter(cf);
}
}
}
@@ -176,7 +176,7 @@
}
if (mCurrDrawable != null) {
mCurrDrawable.jumpToCurrentState();
- mCurrDrawable.setAlpha(mAlpha);
+ mCurrDrawable.mutate().setAlpha(mAlpha);
}
if (mExitAnimationEnd != 0) {
mExitAnimationEnd = 0;
@@ -312,6 +312,7 @@
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
+ d.mutate();
if (mDrawableContainerState.mEnterFadeDuration > 0) {
mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
} else {
@@ -355,13 +356,13 @@
if (mCurrDrawable != null) {
if (mEnterAnimationEnd != 0) {
if (mEnterAnimationEnd <= now) {
- mCurrDrawable.setAlpha(mAlpha);
+ mCurrDrawable.mutate().setAlpha(mAlpha);
mEnterAnimationEnd = 0;
} else {
int animAlpha = (int)((mEnterAnimationEnd-now)*255)
/ mDrawableContainerState.mEnterFadeDuration;
if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
- mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
+ mCurrDrawable.mutate().setAlpha(((255-animAlpha)*mAlpha)/255);
animating = true;
}
}
@@ -378,7 +379,7 @@
int animAlpha = (int)((mExitAnimationEnd-now)*255)
/ mDrawableContainerState.mExitFadeDuration;
if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
- mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
+ mLastDrawable.mutate().setAlpha((animAlpha*mAlpha)/255);
animating = true;
}
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 5b50beb..21344f4 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -124,7 +124,7 @@
private Paint mLayerPaint; // internal, used if we use saveLayer()
private boolean mRectIsDirty; // internal state
- private boolean mMutated;
+ private boolean mMutated = true;
private Path mRingPath;
private boolean mPathIsDirty = true;
@@ -1202,6 +1202,7 @@
mGradientState = state;
initializeWithState(state);
mRectIsDirty = true;
+ mMutated = false;
}
private void initializeWithState(GradientState state) {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 383fe71..03531ac 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -590,6 +590,18 @@
return this;
}
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ if (getLayoutDirection() != layoutDirection) {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i = 0; i < N; i++) {
+ array[i].mDrawable.setLayoutDirection(layoutDirection);
+ }
+ }
+ super.setLayoutDirection(layoutDirection);
+ }
+
static class ChildDrawable {
public Drawable mDrawable;
public int mInsetL, mInsetT, mInsetR, mInsetB;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 62aea71..7a43496 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -77,6 +77,7 @@
@Deprecated
public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null);
+ mMutated = true;
}
/**
@@ -87,6 +88,7 @@
Rect padding, String srcName) {
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res);
mNinePatchState.mTargetDensity = mTargetDensity;
+ mMutated = true;
}
/**
@@ -99,6 +101,7 @@
Rect padding, Rect layoutInsets, String srcName) {
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, layoutInsets), res);
mNinePatchState.mTargetDensity = mTargetDensity;
+ mMutated = true;
}
/**
@@ -109,6 +112,7 @@
@Deprecated
public NinePatchDrawable(NinePatch patch) {
this(new NinePatchState(patch, new Rect()), null);
+ mMutated = true;
}
/**
@@ -118,6 +122,7 @@
public NinePatchDrawable(Resources res, NinePatch patch) {
this(new NinePatchState(patch, new Rect()), res);
mNinePatchState.mTargetDensity = mTargetDensity;
+ mMutated = true;
}
private void setNinePatchState(NinePatchState state, Resources res) {
@@ -181,7 +186,7 @@
}
}
- private Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
+ private static Insets scaleFromDensity(Insets insets, int sdensity, int tdensity) {
int left = Bitmap.scaleFromDensity(insets.left, sdensity, tdensity);
int top = Bitmap.scaleFromDensity(insets.top, sdensity, tdensity);
int right = Bitmap.scaleFromDensity(insets.right, sdensity, tdensity);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 384ca81..a5c3614 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -261,6 +261,15 @@
return this;
}
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ final int numStates = getStateCount();
+ for (int i = 0; i < numStates; i++) {
+ getStateDrawable(i).setLayoutDirection(layoutDirection);
+ }
+ super.setLayoutDirection(layoutDirection);
+ }
+
static final class StateListState extends DrawableContainerState {
int[][] mStateSets;
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
new file mode 100644
index 0000000..41e7e00
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.HashMap;
+
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
+ private Matrix4f mMatrix = new Matrix4f();
+ private Allocation mInput;
+
+ ScriptIntrinsicColorMatrix(int id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ /**
+ * Supported elements types are float, float4, uchar, uchar4
+ *
+ *
+ * @param rs
+ * @param e
+ *
+ * @return ScriptIntrinsicColorMatrix
+ */
+ public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
+ int id = rs.nScriptIntrinsicCreate(2, e.getID(rs));
+ return new ScriptIntrinsicColorMatrix(id, rs);
+
+ }
+
+ public void setColorMatrix(Matrix4f m) {
+ mMatrix.load(m);
+ FieldPacker fp = new FieldPacker(16*4);
+ fp.addMatrix(m);
+ setVar(0, fp);
+ }
+
+ public void forEach(Allocation ain, Allocation aout) {
+ forEach(0, ain, aout, null);
+ }
+
+}
+
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index a073c1a..2109a01 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -230,7 +230,7 @@
if (wnd == NULL) {
} else {
- window = android_Surface_getNativeWindow(_env, wnd).get();
+ window = android_view_Surface_getNativeWindow(_env, wnd).get();
}
rsContextSetSurface(con, width, height, window);
@@ -494,7 +494,7 @@
sp<Surface> s;
if (sur != 0) {
- s = Surface_getSurface(_env, sur);
+ s = android_view_Surface_getSurface(_env, sur);
}
rsAllocationSetSurface(con, alloc, static_cast<ANativeWindow *>(s.get()));
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index fb0b057..e50186d 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -25,12 +25,15 @@
class Surface;
-extern sp<ANativeWindow> android_Surface_getNativeWindow(
- JNIEnv* env, jobject clazz);
-extern bool android_Surface_isInstanceOf(JNIEnv* env, jobject obj);
+/* Gets the underlying ANativeWindow for a Surface. */
+extern sp<ANativeWindow> android_view_Surface_getNativeWindow(
+ JNIEnv* env, jobject surfaceObj);
+
+/* Returns true if the object is an instance of Surface. */
+extern bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj);
/* Gets the underlying Surface from a Surface Java object. */
-extern sp<Surface> Surface_getSurface(JNIEnv* env, jobject thiz);
+extern sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj);
} // namespace android
diff --git a/include/android_runtime/android_view_SurfaceSession.h b/include/android_runtime/android_view_SurfaceSession.h
new file mode 100644
index 0000000..3748f6c
--- /dev/null
+++ b/include/android_runtime/android_view_SurfaceSession.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _ANDROID_VIEW_SURFACE_SESSION_H
+#define _ANDROID_VIEW_SURFACE_SESSION_H
+
+#include "jni.h"
+
+namespace android {
+
+class SurfaceComposerClient;
+
+/* Gets the underlying SurfaceComposerClient for a SurfaceSession. */
+extern sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
+ JNIEnv* env, jobject surfaceSessionObj);
+
+} // namespace android
+
+#endif // _ANDROID_VIEW_SURFACE_SESSION_H
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 23bca3e..48f5bf3 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -957,6 +957,13 @@
SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
+
+ // screenLayout bits for layout direction.
+ MASK_LAYOUTDIR = 0xC0,
+ SHIFT_LAYOUTDIR = 6,
+ LAYOUTDIR_ANY = ACONFIGURATION_LAYOUTDIR_ANY << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_LTR = ACONFIGURATION_LAYOUTDIR_LTR << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_RTL = ACONFIGURATION_LAYOUTDIR_RTL << SHIFT_LAYOUTDIR,
};
enum {
@@ -1020,7 +1027,8 @@
CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE,
CONFIG_VERSION = ACONFIGURATION_VERSION,
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
- CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
+ CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
};
// Compare two configuration, returning CONFIG_* flags set for each value
@@ -1061,7 +1069,7 @@
* There should be one of these chunks for each resource type.
*
* This structure is followed by an array of integers providing the set of
- * configuation change flags (ResTable_config::CONFIG_*) that have multiple
+ * configuration change flags (ResTable_config::CONFIG_*) that have multiple
* resources for that configuration. In addition, the high bit is set if that
* resource has been made public.
*/
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 7b6e540..aabfcae 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -90,6 +90,10 @@
@Override
public Certificate[] engineGetCertificateChain(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
if (leaf == null) {
return null;
@@ -119,6 +123,10 @@
@Override
public Certificate engineGetCertificate(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
if (certificate != null) {
return toCertificate(certificate);
@@ -166,6 +174,10 @@
@Override
public Date engineGetCreationDate(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
if (d != null) {
return d;
@@ -325,7 +337,7 @@
@Override
public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
throws KeyStoreException {
- throw new RuntimeException("Operation not supported because key encoding is unknown");
+ throw new KeyStoreException("Operation not supported because key encoding is unknown");
}
@Override
@@ -334,6 +346,11 @@
throw new KeyStoreException("Entry exists and is not a trusted certificate");
}
+ // We can't set something to null.
+ if (cert == null) {
+ throw new NullPointerException("cert == null");
+ }
+
final byte[] encoded;
try {
encoded = cert.getEncoded();
@@ -348,6 +365,10 @@
@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
+ if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
+ return;
+ }
+
if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
throw new KeyStoreException("No such entry " + alias);
}
@@ -380,6 +401,10 @@
@Override
public boolean engineContainsAlias(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
|| mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
|| mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
@@ -396,12 +421,24 @@
}
private boolean isKeyEntry(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
}
+ private boolean isCertificateEntry(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
+ return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ }
+
@Override
public boolean engineIsCertificateEntry(String alias) {
- return !isKeyEntry(alias) && mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return !isKeyEntry(alias) && isCertificateEntry(alias);
}
@Override
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8cce191..0107da4 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1470,6 +1470,9 @@
if (country[1] != o.country[1]) {
return country[1] < o.country[1] ? -1 : 1;
}
+ if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
+ return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
+ }
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
}
@@ -1558,6 +1561,13 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
+ if (!(screenLayout & MASK_LAYOUTDIR)) return false;
+ if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
if (!smallestScreenWidthDp) return false;
@@ -1683,6 +1693,15 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
+ && (requested->screenLayout & MASK_LAYOUTDIR)) {
+ int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
+ int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
+ return (myLayoutDir > oLayoutDir);
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
// The configuration closest to the actual size is best.
// We assume that larger configs have already been filtered
@@ -1906,6 +1925,12 @@
}
}
if (screenConfig != 0) {
+ const int layoutDir = screenLayout&MASK_LAYOUTDIR;
+ const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
+ if (layoutDir != 0 && layoutDir != setLayoutDir) {
+ return false;
+ }
+
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
// Any screen sizes for larger screens than the setting do not
@@ -2032,6 +2057,21 @@
if (res.size() > 0) res.append("-");
res.append(country, 2);
}
+ if ((screenLayout&MASK_LAYOUTDIR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
+ case ResTable_config::LAYOUTDIR_LTR:
+ res.append("ldltr");
+ break;
+ case ResTable_config::LAYOUTDIR_RTL:
+ res.append("ldrtl");
+ break;
+ default:
+ res.appendFormat("layoutDir=%d",
+ dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
+ break;
+ }
+ }
if (smallestScreenWidthDp != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1947c32..c0f79df 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,8 @@
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES:= \
utils/SortedListImpl.cpp \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -28,6 +30,7 @@
SkiaColorFilter.cpp \
SkiaShader.cpp \
Snapshot.cpp \
+ Stencil.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index f4f56d6..6d27d6e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -38,6 +38,7 @@
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
+#include "Stencil.h"
#include "Dither.h"
namespace android {
@@ -67,6 +68,7 @@
static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
static const GLsizei gAAVertexStride = sizeof(AAVertex);
static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
@@ -252,10 +254,14 @@
TextDropShadowCache dropShadowCache;
FboCache fboCache;
ResourceCache resourceCache;
- Dither dither;
GammaFontRenderer* fontRenderer;
+ Dither dither;
+#if STENCIL_BUFFER_SIZE
+ Stencil stencil;
+#endif
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 27e198c..86667ee 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,583 +25,12 @@
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_CACHE_HEIGHT 256
-#define MAX_TEXT_CACHE_WIDTH 2048
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
-#if DEBUG_FONT_RENDERER
- ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- CacheBlock *currBlock = head;
- CacheBlock *prevBlock = NULL;
- while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
- if (newBlock->mWidth < currBlock->mWidth) {
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- currBlock->mPrev = newBlock;
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
- }
- prevBlock = currBlock;
- currBlock = currBlock->mNext;
- }
- // new block larger than all others - insert at end (but before the remainder space, if there)
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- if (currBlock) {
- currBlock->mPrev = newBlock;
- }
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
-#if DEBUG_FONT_RENDERER
- ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- blockToRemove, blockToRemove->mX, blockToRemove->mY,
- blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
- CacheBlock* newHead = head;
- CacheBlock* nextBlock = blockToRemove->mNext;
- CacheBlock* prevBlock = blockToRemove->mPrev;
- if (prevBlock) {
- prevBlock->mNext = nextBlock;
- } else {
- newHead = nextBlock;
- }
- if (nextBlock) {
- nextBlock->mPrev = prevBlock;
- }
- delete blockToRemove;
- return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
- return false;
- }
-
- uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
- uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
- // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
- // This columns for glyphs that are close but not necessarily exactly the same size. It trades
- // off the loss of a few pixels for some glyphs against the ability to store more glyphs
- // of varying sizes in one block.
- uint16_t roundedUpW =
- (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
- CacheBlock *cacheBlock = mCacheBlocks;
- while (cacheBlock) {
- // Store glyph in this block iff: it fits the block's remaining space and:
- // it's the remainder space (mY == 0) or there's only enough height for this one glyph
- // or it's within ROUNDING_SIZE of the block width
- if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
- (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
- (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
- if (cacheBlock->mHeight - glyphH < glyphH) {
- // Only enough space for this glyph - don't bother rounding up the width
- roundedUpW = glyphW;
- }
- *retOriginX = cacheBlock->mX;
- *retOriginY = cacheBlock->mY;
- // If this is the remainder space, create a new cache block for this column. Otherwise,
- // adjust the info about this column.
- if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
- uint16_t oldX = cacheBlock->mX;
- // Adjust remainder space dimensions
- cacheBlock->mWidth -= roundedUpW;
- cacheBlock->mX += roundedUpW;
- if (mHeight - glyphH >= glyphH) {
- // There's enough height left over to create a new CacheBlock
- CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
- roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
- }
- } else {
- // Insert into current column and adjust column dimensions
- cacheBlock->mY += glyphH;
- cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
- cacheBlock, cacheBlock->mX, cacheBlock->mY,
- cacheBlock->mWidth, cacheBlock->mHeight);
-#endif
- }
- if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
- // If remaining space in this block is too small to be useful, remove it
- mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
- }
- mDirty = true;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: current block list:");
- mCacheBlocks->output();
-#endif
- ++mNumGlyphs;
- return true;
- }
- cacheBlock = cacheBlock->mNext;
- }
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) :
- mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
- mStyle(style), mStrokeWidth(mStrokeWidth) {
-}
-
-
-Font::~Font() {
- for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
- if (mState->mActiveFonts[ct] == this) {
- mState->mActiveFonts.removeAt(ct);
- break;
- }
- }
-
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- if (bounds->bottom > nPenY) {
- bounds->bottom = nPenY;
- }
- if (bounds->left > nPenX) {
- bounds->left = nPenX;
- }
- if (bounds->right < nPenX + width) {
- bounds->right = nPenX + width;
- }
- if (bounds->top < nPenY + height) {
- bounds->top = nPenY + height;
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2,
- nPenX + width, nPenY, u2, v2,
- nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
- uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
-
- CacheTexture *cacheTexture = glyph->mCacheTexture;
- uint32_t cacheWidth = cacheTexture->mWidth;
- const uint8_t* cacheBuffer = cacheTexture->mTexture;
-
- uint32_t cacheX = 0, cacheY = 0;
- int32_t bX = 0, bY = 0;
- for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
- for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
- if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
- ALOGE("Skipping invalid index");
- continue;
- }
-#endif
- uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
- bitmap[bY * bitmapW + bX] = tempCol;
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->fX + destination[0].fX,
- position->fY + destination[0].fY, u1, v2,
- position->fX + destination[1].fX,
- position->fY + destination[1].fY, u2, v2,
- position->fX + destination[2].fX,
- position->fY + destination[2].fY, u2, v1,
- position->fX + destination[3].fX,
- position->fY + destination[3].fY, u1, v1,
- glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
- CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
- if (index >= 0) {
- cachedGlyph = mCachedGlyphs.valueAt(index);
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit);
- }
-
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph);
- }
-
- return cachedGlyph;
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
- bitmapW, bitmapH, NULL, NULL);
- } else {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, NULL);
- }
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, positions);
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- text += start;
-
- int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions) {
- if (bounds == NULL) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
-}
-
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
-
- if (numGlyphs == 0 || text == NULL) {
- return;
- }
- int glyphsCount = 0;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- glyphsCount++;
- }
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- 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 == NULL || len == 0) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {
- &android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph
- };
- RenderGlyph render = gRenderGlyph[mode];
-
- text += start;
- int glyphsCount = 0;
-
- if (CC_LIKELY(positions == NULL)) {
- SkFixed prevRsbDelta = 0;
-
- float penX = x + 0.5f;
- int penY = y;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
- } else {
- const SkPaint::Align align = paint->getTextAlign();
-
- // This is for renderPosText()
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- int penX = x + positions[(glyphsCount << 1)];
- int penY = y + positions[(glyphsCount << 1) + 1];
-
- switch (align) {
- case SkPaint::kRight_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
- break;
- case SkPaint::kCenter_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
- default:
- break;
- }
-
- (*this.*render)(cachedGlyph, penX, penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- glyphsCount++;
- }
- }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- paint->findImage(skiaGlyph);
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
-
- glyph->mBitmapMinU = startX / (float) cacheWidth;
- glyph->mBitmapMinV = startY / (float) cacheHeight;
- glyph->mBitmapMaxU = endX / (float) cacheWidth;
- glyph->mBitmapMaxV = endY / (float) cacheHeight;
-
- mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
- newGlyph->mGlyphIndex = skiaGlyph.fID;
- newGlyph->mIsValid = false;
-
- updateGlyphCache(paint, skiaGlyph, newGlyph);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) {
- Vector<Font*> &activeFonts = state->mActiveFonts;
-
- for (uint32_t i = 0; i < activeFonts.size(); i++) {
- Font* font = activeFonts[i];
- if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle &&
- font->mScaleX == scaleX && font->mStyle == style &&
- (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
- return font;
- }
- }
-
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
- scaleX, style, strokeWidth);
- activeFonts.push(newFont);
- return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -617,7 +46,7 @@
mMaxNumberOfQuads = 1024;
mCurrentQuadIndex = 0;
- mTextMeshPtr = NULL;
+ mTextMesh = NULL;
mCurrentCacheTexture = NULL;
mLastCacheTexture = NULL;
@@ -625,30 +54,40 @@
mIndexBufferID = 0;
- mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
- mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+ mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
+ mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
+ mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
+ mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
mSmallCacheWidth = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
- }
}
- if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
mSmallCacheHeight = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
- }
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
+ mLargeCacheWidth = atoi(property);
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
+ mLargeCacheHeight = atoi(property);
+ }
+
+ uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+ mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+ mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+ mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+ mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
+ if (sLogFontRendererCreate) {
+ INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
+ mSmallCacheWidth, mSmallCacheHeight,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight);
}
sLogFontRendererCreate = false;
@@ -665,7 +104,7 @@
Caches::getInstance().unbindIndicesBuffer();
glDeleteBuffers(1, &mIndexBufferID);
- delete[] mTextMeshPtr;
+ delete[] mTextMesh;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -688,68 +127,34 @@
mCacheTextures[i]->init();
}
- #if DEBUG_FONT_RENDERER
+#if DEBUG_FONT_RENDERER
uint16_t totalGlyphs = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- totalGlyphs += mCacheTextures[i]->mNumGlyphs;
+ totalGlyphs += mCacheTextures[i]->getGlyphCount();
// Erase caches, just as a debugging facility
- if (mCacheTextures[i]->mTexture) {
- memset(mCacheTextures[i]->mTexture, 0,
- mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
+ if (mCacheTextures[i]->getTexture()) {
+ memset(mCacheTextures[i]->getTexture(), 0,
+ mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
}
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
}
-void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
- if (cacheTexture && cacheTexture->mTexture) {
- glDeleteTextures(1, &cacheTexture->mTextureId);
- delete[] cacheTexture->mTexture;
- cacheTexture->mTexture = NULL;
- cacheTexture->mTextureId = 0;
- }
-}
-
void FontRenderer::flushLargeCaches() {
// Start from 1; don't deallocate smallest/default texture
for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mTexture != NULL) {
+ if (cacheTexture->getTexture()) {
cacheTexture->init();
for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
mActiveFonts[j]->invalidateTextureCache(cacheTexture);
}
- deallocateTextureMemory(cacheTexture);
+ cacheTexture->releaseTexture();
}
}
}
-void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
- int width = cacheTexture->mWidth;
- int height = cacheTexture->mHeight;
-
- cacheTexture->mTexture = new uint8_t[width * height];
-
- if (!cacheTexture->mTextureId) {
- glGenTextures(1, &cacheTexture->mTextureId);
- }
-
- Caches::getInstance().activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
-
- const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
uint32_t* startX, uint32_t* startY) {
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
@@ -762,12 +167,12 @@
}
void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
- uint32_t* retOriginX, uint32_t* retOriginY) {
+ uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
checkInit();
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
- mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
+ mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
@@ -779,15 +184,16 @@
CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
- // If the new glyph didn't fit, flush the state so far and invalidate everything
if (!cacheTexture) {
- flushAllAndInvalidate();
+ if (!precaching) {
+ // If the new glyph didn't fit and we are not just trying to precache it,
+ // clear out the cache and try again
+ flushAllAndInvalidate();
+ cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+ }
- // Try to fit it again
- cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
-
- // if we still don't fit, something is wrong and we shouldn't draw
if (!cacheTexture) {
+ // either the glyph didn't fit or we're precaching and will cache it when we draw
return;
}
}
@@ -800,14 +206,15 @@
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = cacheTexture->mWidth;
+ uint32_t cacheWidth = cacheTexture->getWidth();
- if (!cacheTexture->mTexture) {
+ if (!cacheTexture->getTexture()) {
+ Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
- allocateTextureMemory(cacheTexture);
+ cacheTexture->allocateTexture();
}
- uint8_t* cacheBuffer = cacheTexture->mTexture;
+ uint8_t* cacheBuffer = cacheTexture->getTexture();
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
unsigned int stride = glyph.rowBytes();
@@ -847,7 +254,8 @@
CacheTexture* cacheTexture = new CacheTexture(width, height);
if (allocate) {
- allocateTextureMemory(cacheTexture);
+ Caches::getInstance().activeTexture(0);
+ cacheTexture->allocateTexture();
}
return cacheTexture;
@@ -859,21 +267,11 @@
}
mCacheTextures.clear();
- // Next, use other, separate caches for large glyphs.
- uint16_t maxWidth = 0;
- if (Caches::hasInstance()) {
- maxWidth = Caches::getInstance().maxTextureSize;
- }
-
- if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
- maxWidth = MAX_TEXT_CACHE_WIDTH;
- }
-
mUploadTexture = false;
mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
- mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
- mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
- mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
mCurrentCacheTexture = mCacheTextures[0];
}
@@ -907,7 +305,7 @@
uint32_t uvSize = 2;
uint32_t vertsPerQuad = 4;
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
- mTextMeshPtr = new float[vertexBufferSize];
+ mTextMesh = new float[vertexBufferSize];
}
// We don't want to allocate anything unless we actually draw text
@@ -932,16 +330,16 @@
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
+ if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
uint32_t xOffset = 0;
- uint32_t width = cacheTexture->mWidth;
- uint32_t height = cacheTexture->mHeight;
- void* textureData = cacheTexture->mTexture;
+ uint32_t width = cacheTexture->getWidth();
+ uint32_t height = cacheTexture->getHeight();
+ void* textureData = cacheTexture->getTexture();
- if (cacheTexture->mTextureId != lastTextureId) {
+ if (cacheTexture->getTextureId() != lastTextureId) {
+ lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- lastTextureId = cacheTexture->mTextureId;
+ glBindTexture(GL_TEXTURE_2D, lastTextureId);
}
#if DEBUG_FONT_RENDERER
ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
@@ -950,18 +348,14 @@
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
- cacheTexture->mDirty = false;
+ cacheTexture->setDirty(false);
}
}
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
- }
+ glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
+
+ mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
mLastCacheTexture = mCurrentCacheTexture;
mUploadTexture = false;
@@ -973,7 +367,7 @@
Caches& caches = Caches::getInstance();
caches.bindIndicesBuffer(mIndexBufferID);
if (!mDrawn) {
- float* buffer = mTextMeshPtr;
+ float* buffer = mTextMesh;
int offset = 2;
bool force = caches.unbindMeshBuffer();
@@ -1002,7 +396,7 @@
const uint32_t vertsPerQuad = 4;
const uint32_t floatsPerVert = 4;
- float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+ float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
(*currentPos++) = x1;
(*currentPos++) = y1;
@@ -1215,6 +609,19 @@
return mDrawn;
}
+void FontRenderer::removeFont(const Font* font) {
+ for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
+ if (mActiveFonts[ct] == font) {
+ mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ if (mCurrentFont == font) {
+ mCurrentFont = NULL;
+ }
+}
+
void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
// Compute gaussian weights for the blur
// e is the euler's number
@@ -1302,7 +709,6 @@
float currentPixel = 0.0f;
for (int32_t y = 0; y < height; y ++) {
-
uint8_t* output = dest + y * width;
for (int32_t x = 0; x < width; x ++) {
@@ -1336,7 +742,7 @@
}
}
*output = (uint8_t) blurredPixel;
- output ++;
+ output++;
}
}
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index febae17..405db09 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,267 +17,22 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <SkScalerContext.h>
#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
#include <GLES2/gl2.h>
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-#define TEXTURE_BORDER_SIZE 1
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
- uint16_t mX;
- uint16_t mY;
- uint16_t mWidth;
- uint16_t mHeight;
- CacheBlock* mNext;
- CacheBlock* mPrev;
-
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
- mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
- {
- }
-
- static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
-
- static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
-
- void output() {
- CacheBlock *currBlock = this;
- while (currBlock) {
- ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
- currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
- currBlock = currBlock->mNext;
- }
- }
-};
-
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- reset();
- }
-
- void reset() {
- // Delete existing cache blocks
- while (mCacheBlocks != NULL) {
- CacheBlock* tmpBlock = mCacheBlocks;
- mCacheBlocks = mCacheBlocks->mNext;
- delete tmpBlock;
- }
- mNumGlyphs = 0;
- }
-
- void init() {
- // reset, then create a new remainder space to start again
- reset();
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
- bool mDirty;
- uint16_t mNumGlyphs;
- CacheBlock* mCacheBlocks;
-};
-
-struct CachedGlyphInfo {
- // Has the cache been invalidated?
- bool mIsValid;
- // Location of the cached glyph in the bitmap
- // in case we need to resize the texture or
- // render to bitmap
- uint32_t mStartX;
- uint32_t mStartY;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- // Also cache texture coords for the quad
- float mBitmapMinU;
- float mBitmapMinV;
- float mBitmapMaxU;
- float mBitmapMaxV;
- // Minimize how much we call freetype
- uint32_t mGlyphIndex;
- uint32_t mAdvanceX;
- uint32_t mAdvanceY;
- // Values below contain a glyph's origin in the bitmap
- int32_t mBitmapLeft;
- int32_t mBitmapTop;
- // Auto-kerning
- SkFixed mLsbDelta;
- SkFixed mRsbDelta;
- CacheTexture* mCacheTexture;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style {
- kFakeBold = 1
- };
-
- ~Font();
-
- /**
- * Renders the specified string of text.
- * If bitmap is specified, it will be used as the render target
- */
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
- uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
- uint32_t strokeWidth);
-
-protected:
- friend class FontRenderer;
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
- uint32_t, uint32_t, Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void precache(SkPaint* paint, const char* text, int numGlyphs);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
-
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions);
-
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
- uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
-
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
-
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-
- FontRenderer* mState;
- uint32_t mFontId;
- float mFontSize;
- int mFlags;
- uint32_t mItalicStyle;
- uint32_t mScaleX;
- SkPaint::Style mStyle;
- uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -330,31 +85,24 @@
GLuint getTexture(bool linearFiltering = false) {
checkInit();
- if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- mCurrentCacheTexture->mLinearFiltering = linearFiltering;
- mLinearFiltering = linearFiltering;
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ mCurrentCacheTexture->setLinearFiltering(linearFiltering);
+ mLinearFiltering = linearFiltering;
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
-
- return mCurrentCacheTexture->mTextureId;
+ return mCurrentCacheTexture->getTextureId();
}
uint32_t getCacheSize() const {
uint32_t size = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture != NULL && cacheTexture->mTexture != NULL) {
- size += cacheTexture->mWidth * cacheTexture->mHeight;
+ if (cacheTexture && cacheTexture->getTexture()) {
+ size += cacheTexture->getWidth() * cacheTexture->getHeight();
}
}
return size;
}
-protected:
+private:
friend class Font;
const uint8_t* mGammaTable;
@@ -364,7 +112,7 @@
void initTextTexture();
CacheTexture* createCacheTexture(int width, int height, bool allocate);
void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
- uint32_t *retOriginX, uint32_t *retOriginY);
+ uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
@@ -388,8 +136,18 @@
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
+ void removeFont(const Font* font);
+
+ void checkTextureUpdate();
+
+ void setTextureDirty() {
+ mUploadTexture = true;
+ }
+
uint32_t mSmallCacheWidth;
uint32_t mSmallCacheHeight;
+ uint32_t mLargeCacheWidth;
+ uint32_t mLargeCacheHeight;
Vector<CacheTexture*> mCacheTextures;
@@ -399,11 +157,10 @@
CacheTexture* mCurrentCacheTexture;
CacheTexture* mLastCacheTexture;
- void checkTextureUpdate();
bool mUploadTexture;
// Pointer to vertex data to speed up frame to frame work
- float *mTextMeshPtr;
+ float* mTextMesh;
uint32_t mCurrentQuadIndex;
uint32_t mMaxNumberOfQuads;
@@ -417,12 +174,13 @@
bool mLinearFiltering;
- void computeGaussianWeights(float* weights, int32_t radius);
- void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ /** We should consider multi-threading this code or using Renderscript **/
+ static void computeGaussianWeights(float* weights, int32_t radius);
+ static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+ static void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 726b57c7..2e4e349 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -217,10 +217,12 @@
float amount = (pos - start) / distance;
float oppAmount = 1.0f - amount;
- *p++ = uint8_t(startR * oppAmount + endR * amount);
- *p++ = uint8_t(startG * oppAmount + endG * amount);
- *p++ = uint8_t(startB * oppAmount + endB * amount);
- *p++ = uint8_t(startA * oppAmount + endA * amount);
+ const float alpha = startA * oppAmount + endA * amount;
+ const float a = alpha / 255.0f;
+ *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
+ *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
+ *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
+ *p++ = uint8_t(alpha);
}
for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 849c556..b66c898 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -139,10 +139,6 @@
// Setup
///////////////////////////////////////////////////////////////////////////////
-uint32_t OpenGLRenderer::getStencilSize() {
- return STENCIL_BUFFER_SIZE;
-}
-
bool OpenGLRenderer::isDeferred() {
return false;
}
@@ -440,7 +436,7 @@
mode = SkXfermode::kSrcOver_Mode;
}
- createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo);
+ createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo);
}
return count;
@@ -508,44 +504,56 @@
* buffer is left untouched until the first drawing operation. Only when
* something actually gets drawn are the layers regions cleared.
*/
-bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
- float right, float bottom, int alpha, SkXfermode::Mode mode,
- int flags, GLuint previousFbo) {
+bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) {
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
// Window coordinates of the layer
+ Rect clip;
Rect bounds(left, top, right, bottom);
- if (!fboLayer) {
- mSnapshot->transform->mapRect(bounds);
+ Rect untransformedBounds(bounds);
+ mSnapshot->transform->mapRect(bounds);
- // Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*snapshot->clipRect)) {
- // We cannot work with sub-pixels in this case
- bounds.snapToPixelBoundaries();
+ // Layers only make sense if they are in the framebuffer's bounds
+ if (bounds.intersect(*mSnapshot->clipRect)) {
+ // We cannot work with sub-pixels in this case
+ bounds.snapToPixelBoundaries();
- // When the layer is not an FBO, we may use glCopyTexImage so we
- // need to make sure the layer does not extend outside the bounds
- // of the framebuffer
- if (!bounds.intersect(snapshot->previous->viewport)) {
- bounds.setEmpty();
- }
- } else {
+ // When the layer is not an FBO, we may use glCopyTexImage so we
+ // need to make sure the layer does not extend outside the bounds
+ // of the framebuffer
+ if (!bounds.intersect(mSnapshot->previous->viewport)) {
bounds.setEmpty();
+ } else if (fboLayer) {
+ clip.set(bounds);
+ mat4 inverse;
+ inverse.loadInverse(*mSnapshot->transform);
+ inverse.mapRect(clip);
+ clip.snapToPixelBoundaries();
+ if (clip.intersect(untransformedBounds)) {
+ clip.translate(-left, -top);
+ bounds.set(untransformedBounds);
+ } else {
+ clip.setEmpty();
+ }
}
+ } else {
+ bounds.setEmpty();
}
if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
- bounds.getHeight() > mCaches.maxTextureSize) {
- snapshot->empty = fboLayer;
+ bounds.getHeight() > mCaches.maxTextureSize ||
+ (fboLayer && clip.isEmpty())) {
+ mSnapshot->empty = fboLayer;
} else {
- snapshot->invisible = snapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+ mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
}
// Bail out if we won't draw in this snapshot
- if (snapshot->invisible || snapshot->empty) {
+ if (mSnapshot->invisible || mSnapshot->empty) {
return false;
}
@@ -563,23 +571,23 @@
layer->setBlend(true);
// Save the layer in the snapshot
- snapshot->flags |= Snapshot::kFlagIsLayer;
- snapshot->layer = layer;
+ mSnapshot->flags |= Snapshot::kFlagIsLayer;
+ mSnapshot->layer = layer;
if (fboLayer) {
- return createFboLayer(layer, bounds, snapshot, previousFbo);
+ return createFboLayer(layer, bounds, clip, previousFbo);
} else {
// Copy the framebuffer into the layer
layer->bindTexture();
if (!bounds.isEmpty()) {
if (layer->isEmpty()) {
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- bounds.left, snapshot->height - bounds.bottom,
+ bounds.left, mSnapshot->height - bounds.bottom,
layer->getWidth(), layer->getHeight(), 0);
layer->setEmpty(false);
} else {
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
- snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
+ mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
}
// Enqueue the buffer coordinates to clear the corresponding region later
@@ -590,35 +598,20 @@
return true;
}
-bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
- GLuint previousFbo) {
+bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
layer->setFbo(mCaches.fboCache.get());
- snapshot->region = &snapshot->layer->region;
- snapshot->flags |= Snapshot::kFlagFboTarget;
+ mSnapshot->region = &mSnapshot->layer->region;
+ mSnapshot->flags |= Snapshot::kFlagFboTarget;
- Rect clip(bounds);
- snapshot->transform->mapRect(clip);
- clip.intersect(*snapshot->clipRect);
- clip.snapToPixelBoundaries();
- clip.intersect(snapshot->previous->viewport);
-
- mat4 inverse;
- inverse.loadInverse(*mSnapshot->transform);
-
- inverse.mapRect(clip);
- clip.snapToPixelBoundaries();
- clip.intersect(bounds);
- clip.translate(-bounds.left, -bounds.top);
-
- snapshot->flags |= Snapshot::kFlagIsFboLayer;
- snapshot->fbo = layer->getFbo();
- snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
- snapshot->height = bounds.getHeight();
- snapshot->flags |= Snapshot::kFlagDirtyOrtho;
- snapshot->orthoMatrix.load(mOrthoMatrix);
+ mSnapshot->flags |= Snapshot::kFlagIsFboLayer;
+ mSnapshot->fbo = layer->getFbo();
+ mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
+ mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+ mSnapshot->height = bounds.getHeight();
+ mSnapshot->flags |= Snapshot::kFlagDirtyOrtho;
+ mSnapshot->orthoMatrix.load(mOrthoMatrix);
// Bind texture to FBO
glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
@@ -1148,6 +1141,10 @@
mDescription.isAA = true;
}
+void OpenGLRenderer::setupDrawAARect() {
+ mDescription.isAARect = true;
+}
+
void OpenGLRenderer::setupDrawPoint(float pointSize) {
mDescription.isPoint = true;
mDescription.pointSize = pointSize;
@@ -1403,10 +1400,6 @@
int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
-
- // Setting the inverse value saves computations per-fragment in the shader
- int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
- glUniform1f(inverseBoundaryWidthSlot, 1.0f / boundaryWidthProportion);
}
void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
@@ -1752,9 +1745,9 @@
/**
* This function uses a similar approach to that of AA lines in the drawLines() function.
- * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
- * shader to compute the translucency of the color, determined by whether a given pixel is
- * within that boundary region and how far into the region it is.
+ * We expand the rectangle by a half pixel in screen space on all sides. However, instead of using
+ * a fragment shader to compute the translucency of the color from its position, we simply use a
+ * varying parameter to define how far a given pixel is into the region.
*/
void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode) {
@@ -1766,10 +1759,8 @@
Matrix4 *mat = mSnapshot->transform;
float m00 = mat->data[Matrix4::kScaleX];
float m01 = mat->data[Matrix4::kSkewY];
- float m02 = mat->data[2];
float m10 = mat->data[Matrix4::kSkewX];
- float m11 = mat->data[Matrix4::kScaleX];
- float m12 = mat->data[6];
+ float m11 = mat->data[Matrix4::kScaleY];
float scaleX = sqrt(m00 * m00 + m01 * m01);
float scaleY = sqrt(m10 * m10 + m11 * m11);
inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
@@ -1779,6 +1770,11 @@
float boundarySizeX = .5 * inverseScaleX;
float boundarySizeY = .5 * inverseScaleY;
+ float innerLeft = left + boundarySizeX;
+ float innerRight = right - boundarySizeX;
+ float innerTop = top + boundarySizeY;
+ float innerBottom = bottom - boundarySizeY;
+
// Adjust the rect by the AA boundary padding
left -= boundarySizeX;
right += boundarySizeX;
@@ -1788,7 +1784,7 @@
if (!quickReject(left, top, right, bottom)) {
setupDraw();
setupDrawNoTexture();
- setupDrawAALine();
+ setupDrawAARect();
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
setupDrawColorFilter();
setupDrawShader();
@@ -1799,36 +1795,52 @@
setupDrawColorFilterUniforms();
setupDrawShaderIdentityUniforms();
- AAVertex rects[4];
- AAVertex* aaVertices = &rects[0];
- void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
- void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+ AlphaVertex rects[14];
+ AlphaVertex* aVertices = &rects[0];
+ void* alphaCoords = ((GLbyte*) aVertices) + gVertexAlphaOffset;
- int widthSlot;
- int lengthSlot;
+ bool force = mCaches.unbindMeshBuffer();
+ mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
+ aVertices, gAlphaVertexStride);
+ mCaches.resetTexCoordsVertexPointer();
+ mCaches.unbindIndicesBuffer();
- float width = right - left;
- float height = bottom - top;
+ int alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
+ glEnableVertexAttribArray(alphaSlot);
+ glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
- float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
- float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
- boundaryWidthProportion, widthSlot, lengthSlot);
+ // draw left
+ AlphaVertex::set(aVertices++, left, bottom, 0);
+ AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+ AlphaVertex::set(aVertices++, left, top, 0);
+ AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
- int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
- int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
- glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1.0f / boundaryHeightProportion));
+ // draw top
+ AlphaVertex::set(aVertices++, right, top, 0);
+ AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
- AAVertex::set(aaVertices++, left, bottom, 1, 1);
- AAVertex::set(aaVertices++, left, top, 1, 0);
- AAVertex::set(aaVertices++, right, bottom, 0, 1);
- AAVertex::set(aaVertices++, right, top, 0, 0);
+ // draw right
+ AlphaVertex::set(aVertices++, right, bottom, 0);
+ AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
+
+ // draw bottom
+ AlphaVertex::set(aVertices++, left, bottom, 0);
+ AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+
+ // draw inner rect (repeating last vertex to create degenerate bridge triangles)
+ // TODO: also consider drawing the inner rect without the blending-forced shader, if
+ // blending is expensive. Note: can't use drawColorRect() since it doesn't use vertex
+ // buffers like below, resulting in slightly different transformed coordinates.
+ AlphaVertex::set(aVertices++, innerLeft, innerBottom, 1);
+ AlphaVertex::set(aVertices++, innerLeft, innerTop, 1);
+ AlphaVertex::set(aVertices++, innerRight, innerBottom, 1);
+ AlphaVertex::set(aVertices++, innerRight, innerTop, 1);
+
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
- finishDrawAALine(widthSlot, lengthSlot);
+ glDisableVertexAttribArray(alphaSlot);
}
}
@@ -1883,10 +1895,8 @@
Matrix4 *mat = mSnapshot->transform;
float m00 = mat->data[Matrix4::kScaleX];
float m01 = mat->data[Matrix4::kSkewY];
- float m02 = mat->data[2];
float m10 = mat->data[Matrix4::kSkewX];
- float m11 = mat->data[Matrix4::kScaleX];
- float m12 = mat->data[6];
+ float m11 = mat->data[Matrix4::kScaleY];
float scaleX = sqrtf(m00 * m00 + m01 * m01);
float scaleY = sqrtf(m10 * m10 + m11 * m11);
@@ -1946,7 +1956,7 @@
// This value is used in the fragment shader to determine how to fill fragments.
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
- float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+ float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
boundaryWidthProportion, widthSlot, lengthSlot);
}
@@ -1955,9 +1965,7 @@
Vertex* prevVertex = NULL;
int boundaryLengthSlot = -1;
- int inverseBoundaryLengthSlot = -1;
int boundaryWidthSlot = -1;
- int inverseBoundaryWidthSlot = -1;
for (int i = 0; i < count; i += 4) {
// a = start point, b = end point
@@ -2012,9 +2020,9 @@
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
float abLength = abVector.length();
- boundaryLengthProportion = abLength / (length + abLength);
+ boundaryLengthProportion = .5 - abLength / (length + abLength);
} else {
- boundaryLengthProportion = .5 / (length + 1);
+ boundaryLengthProportion = .5 - .5 / (length + 1);
}
abVector /= 2;
@@ -2060,22 +2068,16 @@
if (boundaryWidthSlot < 0) {
boundaryWidthSlot =
mCaches.currentProgram->getUniform("boundaryWidth");
- inverseBoundaryWidthSlot =
- mCaches.currentProgram->getUniform("inverseBoundaryWidth");
}
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
- glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
}
if (boundaryLengthSlot < 0) {
boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
- inverseBoundaryLengthSlot =
- mCaches.currentProgram->getUniform("inverseBoundaryLength");
}
glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
if (prevAAVertex != NULL) {
// Issue two repeat vertices to create degenerate triangles to bridge
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d3b98a4..f2b5f0a 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -208,12 +208,6 @@
SkPaint* filterPaint(SkPaint* paint);
/**
- * Returns the desired size for the stencil buffer. If the returned value
- * is 0, then no stencil buffer is required.
- */
- ANDROID_API static uint32_t getStencilSize();
-
- /**
* Sets the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.
*/
@@ -380,7 +374,7 @@
*
* @return True if the layer was successfully created, false otherwise
*/
- bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom,
+ bool createLayer(float left, float top, float right, float bottom,
int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo);
/**
@@ -391,8 +385,7 @@
* @param bounds The bounds of the layer
* @param previousFbo The name of the current framebuffer
*/
- bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
- GLuint previousFbo);
+ bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo);
/**
* Compose the specified layer as a region.
@@ -441,7 +434,6 @@
* @param color The rectangle's ARGB color, defined as a packed 32 bits word
* @param mode The Skia xfermode to use
* @param ignoreTransform True if the current transform should be ignored
- * @param ignoreBlending True if the blending is set by the caller
*/
void drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode, bool ignoreTransform = false);
@@ -656,6 +648,7 @@
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
void setupDrawAALine();
+ void setupDrawAARect();
void setupDrawPoint(float pointSize);
void setupDrawColor(int color);
void setupDrawColor(int color, int alpha);
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 1818f82..a3bfaa4 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -81,6 +81,8 @@
#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_IS_AA_RECT_SHIFT 42
+
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -128,6 +130,7 @@
bool isBitmapNpot;
bool isAA;
+ bool isAARect;
bool hasGradient;
Gradient gradientType;
@@ -165,6 +168,7 @@
hasTextureTransform = false;
isAA = false;
+ isAARect = false;
modulate = false;
@@ -260,6 +264,7 @@
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
+ if (isAARect) key |= programid(0x1) << PROGRAM_IS_AA_RECT_SHIFT;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index d67bfbe..0ed8008 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -43,6 +43,8 @@
const char* gVS_Header_Attributes_AAParameters =
"attribute float vtxWidth;\n"
"attribute float vtxLength;\n";
+const char* gVS_Header_Attributes_AARectParameters =
+ "attribute float vtxAlpha;\n";
const char* gVS_Header_Uniforms_TextureTransform =
"uniform mat4 mainTextureTransform;\n";
const char* gVS_Header_Uniforms =
@@ -65,6 +67,8 @@
const char* gVS_Header_Varyings_IsAA =
"varying float widthProportion;\n"
"varying float lengthProportion;\n";
+const char* gVS_Header_Varyings_IsAARect =
+ "varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
const char* gVS_Header_Varyings_PointHasBitmap =
@@ -112,6 +116,8 @@
const char* gVS_Main_AA =
" widthProportion = vtxWidth;\n"
" lengthProportion = vtxLength;\n";
+const char* gVS_Main_AARect =
+ " alpha = vtxAlpha;\n";
const char* gVS_Footer =
"}\n\n";
@@ -129,9 +135,7 @@
"uniform vec4 color;\n";
const char* gFS_Uniforms_AA =
"uniform float boundaryWidth;\n"
- "uniform float inverseBoundaryWidth;\n"
- "uniform float boundaryLength;\n"
- "uniform float inverseBoundaryLength;\n";
+ "uniform float boundaryLength;\n";
const char* gFS_Header_Uniforms_PointHasBitmap =
"uniform vec2 textureDimension;\n"
"uniform float pointSize;\n";
@@ -242,16 +246,11 @@
const char* gFS_Main_ModulateColor_ApplyGamma =
" fragColor *= pow(color.a, gamma);\n";
const char* gFS_Main_AccountForAA =
- " if (widthProportion < boundaryWidth) {\n"
- " fragColor *= (widthProportion * inverseBoundaryWidth);\n"
- " } else if (widthProportion > (1.0 - boundaryWidth)) {\n"
- " fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n"
- " }\n"
- " if (lengthProportion < boundaryLength) {\n"
- " fragColor *= (lengthProportion * inverseBoundaryLength);\n"
- " } else if (lengthProportion > (1.0 - boundaryLength)) {\n"
- " fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n"
- " }\n";
+ " fragColor *= (1.0 - smoothstep(boundaryWidth, 0.5, abs(0.5 - widthProportion)))\n"
+ " * (1.0 - smoothstep(boundaryLength, 0.5, abs(0.5 - lengthProportion)));\n";
+const char* gFS_Main_AccountForAARect =
+ " fragColor *= alpha;\n";
+
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
" fragColor = texture2D(sampler, outTexCoords);\n",
@@ -448,7 +447,9 @@
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
- if (description.isAA) {
+ if (description.isAARect) {
+ shader.append(gVS_Header_Attributes_AARectParameters);
+ } else if (description.isAA) {
shader.append(gVS_Header_Attributes_AAParameters);
}
// Uniforms
@@ -469,7 +470,9 @@
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAA) {
+ if (description.isAARect) {
+ shader.append(gVS_Header_Varyings_IsAARect);
+ } else if (description.isAA) {
shader.append(gVS_Header_Varyings_IsAA);
}
if (description.hasGradient) {
@@ -488,7 +491,9 @@
} else if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Main_OutTexCoords);
}
- if (description.isAA) {
+ if (description.isAARect) {
+ shader.append(gVS_Main_AARect);
+ } else if (description.isAA) {
shader.append(gVS_Main_AA);
}
if (description.hasGradient) {
@@ -530,7 +535,9 @@
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
- if (description.isAA) {
+ if (description.isAARect) {
+ shader.append(gVS_Header_Varyings_IsAARect);
+ } else if (description.isAA) {
shader.append(gVS_Header_Varyings_IsAA);
}
if (description.hasGradient) {
@@ -571,7 +578,8 @@
// Optimization for common cases
if (!description.isAA && !blendFramebuffer &&
- description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
+ description.colorOp == ProgramDescription::kColorNone &&
+ !description.isPoint && !description.isAARect) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -663,7 +671,9 @@
shader.append(gFS_Main_FetchColor);
}
}
- if (description.isAA) {
+ if (description.isAARect) {
+ shader.append(gFS_Main_AccountForAARect);
+ } else if (description.isAA) {
shader.append(gFS_Main_AccountForAA);
}
if (description.hasGradient) {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6b6dc9e..0e3268e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -74,8 +74,10 @@
#define PROPERTY_TEXTURE_CACHE_FLUSH_RATE "ro.hwui.texture_cache_flush_rate"
// These properties are defined in pixels
-#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
-#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
+#define PROPERTY_TEXT_SMALL_CACHE_WIDTH "ro.hwui.text_small_cache_width"
+#define PROPERTY_TEXT_SMALL_CACHE_HEIGHT "ro.hwui.text_small_cache_height"
+#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
+#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
// Indicates whether gamma correction should be applied in the shaders
// or in lookup tables. Accepted values:
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 8916efd..9013fd5 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -46,11 +46,12 @@
}
static inline void bindUniformColor(int slot, uint32_t color) {
+ const float a = ((color >> 24) & 0xff) / 255.0f;
glUniform4f(slot,
- ((color >> 16) & 0xff) / 255.0f,
- ((color >> 8) & 0xff) / 255.0f,
- ((color ) & 0xff) / 255.0f,
- ((color >> 24) & 0xff) / 255.0f);
+ a * ((color >> 16) & 0xff) / 255.0f,
+ a * ((color >> 8) & 0xff) / 255.0f,
+ a * ((color ) & 0xff) / 255.0f,
+ a);
}
///////////////////////////////////////////////////////////////////////////////
@@ -154,10 +155,6 @@
// Uniforms
bindTexture(texture, mWrapS, mWrapT);
- // Assume linear here; we should really check the transform in
- // ::updateTransforms() but we don't have the texture object
- // available at that point. The optimization is not worth the
- // effort for now.
texture->setFilter(GL_LINEAR);
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
@@ -166,14 +163,6 @@
glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
}
-void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, modelView);
- glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
- GL_FALSE, &textureTransform.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Linear gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -257,13 +246,6 @@
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
-void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Circular gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -384,13 +366,6 @@
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
-void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Compose shader
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index a710b86..2687592 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -82,10 +82,6 @@
mGradientCache = gradientCache;
}
- virtual void updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- }
-
uint32_t getGenerationId() {
return mGenerationId;
}
@@ -148,7 +144,6 @@
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
SkiaBitmapShader() {
@@ -172,7 +167,6 @@
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
SkiaLinearGradientShader() {
@@ -197,7 +191,6 @@
virtual void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
protected:
SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 5d5961a..4484676 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -57,7 +57,7 @@
clipRect = &mClipRectRoot;
#if STENCIL_BUFFER_SIZE
if (s->clipRegion) {
- mClipRegionRoot.merge(*s->clipRegion);
+ mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
clipRegion = &mClipRegionRoot;
}
#endif
@@ -84,8 +84,7 @@
#if STENCIL_BUFFER_SIZE
if (!clipRegion) {
clipRegion = &mClipRegionRoot;
- android::Rect tmp(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
- clipRegion->set(tmp);
+ clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
}
#endif
}
@@ -93,11 +92,11 @@
void Snapshot::copyClipRectFromRegion() {
#if STENCIL_BUFFER_SIZE
if (!clipRegion->isEmpty()) {
- android::Rect bounds(clipRegion->bounds());
- clipRect->set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ const SkIRect& bounds = clipRegion->getBounds();
+ clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
if (clipRegion->isRect()) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
} else {
@@ -107,43 +106,11 @@
#endif
}
-bool Snapshot::clipRegionOr(float left, float top, float right, float bottom) {
+bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->orSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionXor(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->xorSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionAnd(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->andSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionNand(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->subtractSelf(tmp);
+ SkIRect tmp;
+ tmp.set(left, top, right, bottom);
+ clipRegion->op(tmp, op);
copyClipRectFromRegion();
return true;
#else
@@ -161,14 +128,9 @@
bool clipped = false;
switch (op) {
- case SkRegion::kDifference_Op: {
- ensureClipRegion();
- clipped = clipRegionNand(r.left, r.top, r.right, r.bottom);
- break;
- }
case SkRegion::kIntersect_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionOr(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
} else {
clipped = clipRect->intersect(r);
if (!clipped) {
@@ -180,26 +142,22 @@
}
case SkRegion::kUnion_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionAnd(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
} else {
clipped = clipRect->unionWith(r);
}
break;
}
- case SkRegion::kXOR_Op: {
- ensureClipRegion();
- clipped = clipRegionXor(r.left, r.top, r.right, r.bottom);
- break;
- }
- case SkRegion::kReverseDifference_Op: {
- // TODO!!!!!!!
- break;
- }
case SkRegion::kReplace_Op: {
setClip(r.left, r.top, r.right, r.bottom);
clipped = true;
break;
}
+ default: {
+ ensureClipRegion();
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
+ break;
+ }
}
if (clipped) {
@@ -213,7 +171,7 @@
clipRect->set(left, top, right, bottom);
#if STENCIL_BUFFER_SIZE
if (clipRegion) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
#endif
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 30b03fc..a89b740 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -198,7 +198,7 @@
*
* This field is used only if STENCIL_BUFFER_SIZE is > 0.
*/
- Region* clipRegion;
+ SkRegion* clipRegion;
/**
* The ancestor layer's dirty region.
@@ -223,17 +223,14 @@
void ensureClipRegion();
void copyClipRectFromRegion();
- bool clipRegionOr(float left, float top, float right, float bottom);
- bool clipRegionXor(float left, float top, float right, float bottom);
- bool clipRegionAnd(float left, float top, float right, float bottom);
- bool clipRegionNand(float left, float top, float right, float bottom);
+ bool clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op);
mat4 mTransformRoot;
Rect mClipRectRoot;
Rect mLocalClip;
#if STENCIL_BUFFER_SIZE
- Region mClipRegionRoot;
+ SkRegion mClipRegionRoot;
#endif
}; // class Snapshot
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
new file mode 100644
index 0000000..9d2c86f
--- /dev/null
+++ b/libs/hwui/Stencil.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <GLES2/gl2.h>
+
+#include "Properties.h"
+#include "Stencil.h"
+
+namespace android {
+namespace uirenderer {
+
+Stencil::Stencil(): mState(kDisabled) {
+}
+
+uint32_t Stencil::getStencilSize() {
+ return STENCIL_BUFFER_SIZE;
+}
+
+void Stencil::clear() {
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+}
+
+void Stencil::enableTest() {
+ if (mState != kTest) {
+ enable();
+ glStencilFunc(GL_LESS, 0x0, 0x1);
+ // We only want to test, let's keep everything
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ mState = kTest;
+ }
+}
+
+void Stencil::enableWrite() {
+ if (mState != kWrite) {
+ enable();
+ glStencilFunc(GL_ALWAYS, 0x1, 0x1);
+ // The test always passes so the first two values are meaningless
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ mState = kWrite;
+ }
+}
+
+void Stencil::enable() {
+ if (!mState == kDisabled) {
+ glEnable(GL_STENCIL_TEST);
+ }
+}
+
+void Stencil::disable() {
+ if (mState != kDisabled) {
+ glDisable(GL_STENCIL_TEST);
+ mState = kDisabled;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
new file mode 100644
index 0000000..67ccc78
--- /dev/null
+++ b/libs/hwui/Stencil.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_HWUI_STENCIL_H
+#define ANDROID_HWUI_STENCIL_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <cutils/compiler.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stencil buffer management
+///////////////////////////////////////////////////////////////////////////////
+
+class ANDROID_API Stencil {
+public:
+ Stencil();
+
+ /**
+ * Returns the desired size for the stencil buffer. If the returned value
+ * is 0, then no stencil buffer is required.
+ */
+ ANDROID_API static uint32_t getStencilSize();
+
+ /**
+ * Clears the stencil buffer.
+ */
+ void clear();
+
+ /**
+ * Enables stencil test. When the stencil test is enabled the stencil
+ * buffer is not written into.
+ */
+ void enableTest();
+
+ /**
+ * Enables stencil write. When stencil write is enabled, the stencil
+ * test always succeeds and the value 0x1 is written in the stencil
+ * buffer for each fragment.
+ */
+ void enableWrite();
+
+ /**
+ * Disables stencil test and write.
+ */
+ void disable();
+
+ /**
+ * Indicates whether either test or write is enabled.
+ */
+ bool isEnabled() {
+ return mState != kDisabled;
+ }
+
+private:
+ void enable();
+
+ enum StencilState {
+ kDisabled,
+ kTest,
+ kWrite
+ };
+
+ StencilState mState;
+
+}; // class Stencil
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_STENCIL_H
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 0000000..4a3af12
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+
+ CacheBlock* currBlock = head;
+ CacheBlock* prevBlock = NULL;
+
+ while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+ if (newBlock->mWidth < currBlock->mWidth) {
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ currBlock->mPrev = newBlock;
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+ }
+
+ prevBlock = currBlock;
+ currBlock = currBlock->mNext;
+ }
+
+ // new block larger than all others - insert at end (but before the remainder space, if there)
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+
+ if (currBlock) {
+ currBlock->mPrev = newBlock;
+ }
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ blockToRemove, blockToRemove->mX, blockToRemove->mY,
+ blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+
+ CacheBlock* newHead = head;
+ CacheBlock* nextBlock = blockToRemove->mNext;
+ CacheBlock* prevBlock = blockToRemove->mPrev;
+
+ if (prevBlock) {
+ prevBlock->mNext = nextBlock;
+ } else {
+ newHead = nextBlock;
+ }
+
+ if (nextBlock) {
+ nextBlock->mPrev = prevBlock;
+ }
+
+ delete blockToRemove;
+
+ return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
+ return false;
+ }
+
+ uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+ uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+
+ // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+ // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+ // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+ // of varying sizes in one block.
+ uint16_t roundedUpW = (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+
+ CacheBlock* cacheBlock = mCacheBlocks;
+ while (cacheBlock) {
+ // Store glyph in this block iff: it fits the block's remaining space and:
+ // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+ // or it's within ROUNDING_SIZE of the block width
+ if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+ (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+ (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+ if (cacheBlock->mHeight - glyphH < glyphH) {
+ // Only enough space for this glyph - don't bother rounding up the width
+ roundedUpW = glyphW;
+ }
+
+ *retOriginX = cacheBlock->mX;
+ *retOriginY = cacheBlock->mY;
+
+ // If this is the remainder space, create a new cache block for this column. Otherwise,
+ // adjust the info about this column.
+ if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+ uint16_t oldX = cacheBlock->mX;
+ // Adjust remainder space dimensions
+ cacheBlock->mWidth -= roundedUpW;
+ cacheBlock->mX += roundedUpW;
+
+ if (mHeight - glyphH >= glyphH) {
+ // There's enough height left over to create a new CacheBlock
+ CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+ roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+ }
+ } else {
+ // Insert into current column and adjust column dimensions
+ cacheBlock->mY += glyphH;
+ cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ cacheBlock, cacheBlock->mX, cacheBlock->mY,
+ cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+ }
+
+ if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+ // If remaining space in this block is too small to be useful, remove it
+ mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+ }
+
+ mDirty = true;
+ mNumGlyphs++;
+
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: current block list:");
+ mCacheBlocks->output();
+#endif
+
+ return true;
+ }
+ cacheBlock = cacheBlock->mNext;
+ }
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 0000000..bf1f4a9
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+ uint16_t mX;
+ uint16_t mY;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ CacheBlock* mNext;
+ CacheBlock* mPrev;
+
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
+ }
+
+ static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
+
+ static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
+
+ void output() {
+ CacheBlock* currBlock = this;
+ while (currBlock) {
+ ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+ currBlock = currBlock->mNext;
+ }
+ }
+};
+
+class CacheTexture {
+public:
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ ~CacheTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
+ reset();
+ }
+
+ void reset() {
+ // Delete existing cache blocks
+ while (mCacheBlocks != NULL) {
+ CacheBlock* tmpBlock = mCacheBlocks;
+ mCacheBlocks = mCacheBlocks->mNext;
+ delete tmpBlock;
+ }
+ mNumGlyphs = 0;
+ }
+
+ void init() {
+ // reset, then create a new remainder space to start again
+ reset();
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ void releaseTexture() {
+ if (mTexture) {
+ glDeleteTextures(1, &mTextureId);
+ delete[] mTexture;
+ mTexture = NULL;
+ mTextureId = 0;
+ }
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void allocateTexture() {
+ int width = mWidth;
+ int height = mHeight;
+
+ mTexture = new uint8_t[width * height];
+
+ if (!mTextureId) {
+ glGenTextures(1, &mTextureId);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimensions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
+
+ inline uint16_t getWidth() const {
+ return mWidth;
+ }
+
+ inline uint16_t getHeight() const {
+ return mHeight;
+ }
+
+ inline uint8_t* getTexture() const {
+ return mTexture;
+ }
+
+ inline GLuint getTextureId() const {
+ return mTextureId;
+ }
+
+ inline bool isDirty() const {
+ return mDirty;
+ }
+
+ inline void setDirty(bool dirty) {
+ mDirty = dirty;
+ }
+
+ inline bool getLinearFiltering() const {
+ return mLinearFiltering;
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void setLinearFiltering(bool linearFiltering, bool bind = true) {
+ if (linearFiltering != mLinearFiltering) {
+ mLinearFiltering = linearFiltering;
+
+ const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ }
+ }
+
+ inline uint16_t getGlyphCount() const {
+ return mNumGlyphs;
+ }
+
+private:
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
+ bool mDirty;
+ uint16_t mNumGlyphs;
+ CacheBlock* mCacheBlocks;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 0000000..6680a00
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture or
+ // render to bitmap
+ uint32_t mStartX;
+ uint32_t mStartY;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ int32_t mBitmapLeft;
+ int32_t mBitmapTop;
+ // Auto-kerning
+ SkFixed mLsbDelta;
+ SkFixed mRsbDelta;
+ CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 0000000..6e205b8
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) :
+ mState(state), mFontId(fontId), mFontSize(fontSize),
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+ mStyle(style), mStrokeWidth(mStrokeWidth) {
+}
+
+
+Font::~Font() {
+ mState->removeFont(this);
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ delete mCachedGlyphs.valueAt(i);
+ }
+}
+
+void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
+ cachedGlyph->mIsValid = false;
+ }
+ }
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ if (bounds->bottom > nPenY) {
+ bounds->bottom = nPenY;
+ }
+ if (bounds->left > nPenX) {
+ bounds->left = nPenX;
+ }
+ if (bounds->right < nPenX + width) {
+ bounds->right = nPenX + width;
+ }
+ if (bounds->top < nPenY + height) {
+ bounds->top = nPenY + height;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+ uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+ CacheTexture* cacheTexture = glyph->mCacheTexture;
+ uint32_t cacheWidth = cacheTexture->getWidth();
+ const uint8_t* cacheBuffer = cacheTexture->getTexture();
+
+ uint32_t cacheX = 0, cacheY = 0;
+ int32_t bX = 0, bY = 0;
+ for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
+ if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+ ALOGE("Skipping invalid index");
+ continue;
+ }
+#endif
+ uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+ bitmap[bY * bitmapW + bX] = tempCol;
+ }
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+ const float halfWidth = glyph->mBitmapWidth * 0.5f;
+ const float height = glyph->mBitmapHeight;
+
+ vOffset += glyph->mBitmapTop + height;
+
+ SkPoint destination[4];
+ measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
+
+ // Move along the tangent and offset by the normal
+ destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+ -tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+ tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[2].set(destination[1].fX + tangent->fY * height,
+ destination[1].fY - tangent->fX * height);
+ destination[3].set(destination[0].fX + tangent->fY * height,
+ destination[0].fY - tangent->fX * height);
+
+ const float u1 = glyph->mBitmapMinU;
+ const float u2 = glyph->mBitmapMaxU;
+ const float v1 = glyph->mBitmapMinV;
+ const float v2 = glyph->mBitmapMaxV;
+
+ mState->appendRotatedMeshQuad(
+ position->fX + destination[0].fX,
+ position->fY + destination[0].fY, u1, v2,
+ position->fX + destination[1].fX,
+ position->fY + destination[1].fY, u2, v2,
+ position->fX + destination[2].fX,
+ position->fY + destination[2].fY, u2, v1,
+ position->fX + destination[3].fX,
+ position->fY + destination[3].fY, u1, v1,
+ glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
+ cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
+
+ return cachedGlyph;
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL, NULL);
+ } else {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, NULL);
+ }
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions) {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, positions);
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ text += start;
+
+ int glyphsCount = 0;
+ SkFixed prevRsbDelta = 0;
+
+ float penX = 0.0f;
+
+ SkPoint position;
+ SkVector tangent;
+
+ SkPathMeasure measure(*path, false);
+ float pathLength = SkScalarToFloat(measure.getLength());
+
+ if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+ float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float pathOffset = pathLength;
+ if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+ textWidth *= 0.5f;
+ pathOffset *= 0.5f;
+ }
+ penX += pathOffset - textWidth;
+ }
+
+ while (glyphsCount < numGlyphs && penX < pathLength) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions) {
+ if (bounds == NULL) {
+ ALOGE("No return rectangle provided to measure text");
+ return;
+ }
+ bounds->set(1e6, -1e6, -1e6, 1e6);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+ if (numGlyphs == 0 || text == NULL) {
+ return;
+ }
+ int glyphsCount = 0;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
+
+ glyphsCount++;
+ }
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ 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 == NULL || len == 0) {
+ return;
+ }
+
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
+
+ text += start;
+ int glyphsCount = 0;
+
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
+
+ float penX = x + 0.5f;
+ int penY = y;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+ } else {
+ const SkPaint::Align align = paint->getTextAlign();
+
+ // This is for renderPosText()
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ int penX = x + positions[(glyphsCount << 1)];
+ int penY = y + positions[(glyphsCount << 1) + 1];
+
+ switch (align) {
+ case SkPaint::kRight_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+ break;
+ case SkPaint::kCenter_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+ default:
+ break;
+ }
+
+ (*this.*render)(cachedGlyph, penX, penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ glyphsCount++;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+ glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+ glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mStartX = startX;
+ glyph->mStartY = startY;
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
+ uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
+
+ glyph->mBitmapMinU = startX / (float) cacheWidth;
+ glyph->mBitmapMinV = startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+ mState->setTextureDirty();
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize &&
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX && font->mStyle == style &&
+ (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+ scaleX, style, strokeWidth);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 0000000..7cab31e
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_HWUI_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ enum Style {
+ kFakeBold = 1
+ };
+
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ * If bitmap is specified, it will be used as the render target
+ */
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+ uint32_t strokeWidth);
+
+private:
+ friend class FontRenderer;
+ typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+ uint32_t, uint32_t, Rect*, const float*);
+
+ enum RenderMode {
+ FRAMEBUFFER,
+ BITMAP,
+ MEASURE,
+ };
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
+
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions);
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
+
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
+
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching);
+
+ void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+ int mFlags;
+ uint32_t mItalicStyle;
+ uint32_t mScaleX;
+ SkPaint::Style mStyle;
+ uint32_t mStrokeWidth;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 0000000..12247ba
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_HWUI_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define TO_GLYPH(g) g
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
+#else
+ typedef SkUnichar glyph_t;
+ #define TO_GLYPH(g) ((SkUnichar) g)
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 84fb0dd..aea8a88 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -57,6 +57,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -930,6 +931,24 @@
return delta;
}
+ private void sendBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void sendStickyBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
// UI update and Broadcast Intent
private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
@@ -944,7 +963,7 @@
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
- mContext.sendBroadcast(intent);
+ sendBroadcastToAll(intent);
}
// UI update and Broadcast Intent
@@ -954,7 +973,7 @@
Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
- mContext.sendBroadcast(intent);
+ sendBroadcastToAll(intent);
}
// UI update and Broadcast Intent
@@ -968,9 +987,7 @@
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(intent);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(intent);
}
/**
@@ -1992,7 +2009,7 @@
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
mScoConnectionState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
mScoConnectionState = state;
}
}
@@ -2283,9 +2300,7 @@
broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(broadcast);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(broadcast);
}
private void broadcastVibrateSetting(int vibrateType) {
@@ -2294,7 +2309,7 @@
Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
- mContext.sendBroadcast(broadcast);
+ sendBroadcastToAll(broadcast);
}
}
@@ -3175,7 +3190,7 @@
}
private void sendBecomingNoisyIntent() {
- mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
+ sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}
// must be called synchronized on mConnectedDevices
@@ -3294,7 +3309,9 @@
// sent if none of these devices is connected.
int mBecomingNoisyIntentDevices =
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
- AudioSystem.DEVICE_OUT_ALL_A2DP;
+ AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ALL_USB;
// must be called before removing the device from mConnectedDevices
private int checkSendBecomingNoisyIntent(int device, int state) {
@@ -3364,7 +3381,12 @@
}
}
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void onSetWiredDeviceConnectionState(int device, int state, String name)
@@ -3516,7 +3538,7 @@
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
@@ -3533,7 +3555,7 @@
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
@@ -3924,8 +3946,13 @@
mMediaEventWakeLock.acquire();
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -3958,8 +3985,14 @@
if (needWakeLock) {
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone,
+ mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 4b8d3cb..749ef12 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -45,7 +45,7 @@
* }
* ByteBuffer inputBuffer = ByteBuffer.allocate(...)
* while (extractor.readSampleData(inputBuffer, ...) >= 0) {
- * int trackIndex = extractor.getTrackIndex();
+ * int trackIndex = extractor.getSampleTrackIndex();
* long presentationTimeUs = extractor.getSampleTime();
* ...
* extractor.advance();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0d7b45e..88cf4ac 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1415,7 +1415,8 @@
long lastModifiedSeconds = file.lastModified() / 1000;
if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
- !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
+ !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
+ !MediaFile.isDrmFileType(fileType)) {
// no need to use the media scanner, but we need to update last modified and file size
ContentValues values = new ContentValues();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4941ae5..f91c9a0 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -370,7 +370,7 @@
sp<ISurfaceTexture> surfaceTexture;
if (jsurface != NULL) {
- sp<Surface> surface(Surface_getSurface(env, jsurface));
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
surfaceTexture = surface->getSurfaceTexture();
} else {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c2a6889..04ba348 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -271,7 +271,7 @@
sp<ISurfaceTexture> new_st;
if (jsurface) {
- sp<Surface> surface(Surface_getSurface(env, jsurface));
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
new_st = surface->getSurfaceTexture();
new_st->incStrong(thiz);
diff --git a/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
index d5c7aaa..f977e60 100644
--- a/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
+++ b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
@@ -91,6 +91,9 @@
if (parameterKey.equals("source")) {
Filter background = mGraph.getFilter("background");
background.setInputValue("sourceUrl", value);
+ } else if (parameterKey.equals("context")) {
+ Filter background = mGraph.getFilter("background");
+ background.setInputValue("context", value);
}
}
diff --git a/media/tests/EffectsTest/Android.mk b/media/tests/EffectsTest/Android.mk
new file mode 100755
index 0000000..25b4fe4
--- /dev/null
+++ b/media/tests/EffectsTest/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := EffectsTest
+
+include $(BUILD_PACKAGE)
diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml
new file mode 100755
index 0000000..9b59891
--- /dev/null
+++ b/media/tests/EffectsTest/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.effectstest">
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+
+ <application>
+ <activity android:label="@string/app_name"
+ android:name="EffectsTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:label="@string/envreverb_test_name"
+ android:name="EnvReverbTest">
+ </activity>
+
+ <activity android:label="@string/presetrvb_test_name"
+ android:name="PresetReverbTest">
+ </activity>
+
+ <activity android:label="@string/equalizer_test_name"
+ android:name="EqualizerTest">
+ </activity>
+
+ <activity android:label="@string/virtualizer_test_name"
+ android:name="VirtualizerTest">
+ </activity>
+
+ <activity android:label="@string/bassboost_test_name"
+ android:name="BassBoostTest">
+ </activity>
+
+ <activity android:label="@string/visualizer_test_name"
+ android:name="VisualizerTest">
+ </activity>
+
+ </application>
+</manifest>
diff --git a/media/tests/EffectsTest/res/drawable/icon.png b/media/tests/EffectsTest/res/drawable/icon.png
new file mode 100755
index 0000000..64e3601
--- /dev/null
+++ b/media/tests/EffectsTest/res/drawable/icon.png
Binary files differ
diff --git a/media/tests/EffectsTest/res/drawable/stop.png b/media/tests/EffectsTest/res/drawable/stop.png
new file mode 100755
index 0000000..83f012c
--- /dev/null
+++ b/media/tests/EffectsTest/res/drawable/stop.png
Binary files differ
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
new file mode 100755
index 0000000..0888e98
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/bbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/bbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/bbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/bassboostOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/bbStrengthName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/stength_name" />
+
+ <LinearLayout android:id="@+id/bbStrengthDesc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="30dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbStrengthMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="left"
+ android:gravity="left"/>
+ <TextView android:id="@+id/bbStrengthMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:gravity="right"/>
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/bbStrengthSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/bbStrengthValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/effectstest.xml b/media/tests/EffectsTest/res/layout/effectstest.xml
new file mode 100755
index 0000000..9af4eb6
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/effectstest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <Button android:id="@+id/env_reverb_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/envreverb_test_name">
+ </Button>
+
+ <Button android:id="@+id/preset_reverb_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/presetrvb_test_name">
+ </Button>
+
+ <Button android:id="@+id/equalizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/equalizer_test_name">
+ </Button>
+
+ <Button android:id="@+id/virtualizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/virtualizer_test_name">
+ </Button>
+
+ <Button android:id="@+id/bassboost_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/bassboost_test_name">
+ </Button>
+
+ <Button android:id="@+id/visualizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/visualizer_test_name">
+ </Button>
+
+ <ListView android:id="@+id/effect_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false"/>
+
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/envreverbtest.xml b/media/tests/EffectsTest/res/layout/envreverbtest.xml
new file mode 100755
index 0000000..01c3240
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/envreverbtest.xml
@@ -0,0 +1,553 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/rvbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/rvbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/rvbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/rvbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/rvbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/rvbOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/auxFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginRight="3dip"
+ android:layout_marginBottom="3dip" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_gravity="left"
+ android:layout_weight="1.0"
+ android:orientation="vertical"
+ android:layout_marginLeft="1dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginRight="1dip"
+ android:layout_marginBottom="1dip"
+ >
+
+ <LinearLayout android:id="@+id/playPauseFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginRight="3dip"
+ android:layout_marginBottom="1dip" >
+
+ <ImageButton android:id="@+id/stop1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:layout_weight="0.0"
+ android:src="@drawable/stop"/>
+
+ <ImageButton android:id="@+id/playPause1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center"
+ android:layout_weight="0.0"
+ android:src="@android:drawable/ic_media_play"/>
+
+ <ToggleButton android:id="@+id/attachButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0"
+ android:textOff="@string/effect_attach_off"
+ android:textOn="@string/effect_attach_on" />
+ </LinearLayout>
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:layout_gravity="right"
+ android:layout_weight="1.0"
+ android:layout_marginLeft="3dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginRight="1dip"
+ android:layout_marginBottom="3dip"
+ >
+
+ <TextView android:id="@+id/sendLevelText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/send_level_name" />
+
+ <SeekBar android:id="@+id/sendLevelSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/sendLevelValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_1_name" />
+
+ <SeekBar android:id="@+id/rvbParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam2Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_2_name" />
+
+ <SeekBar android:id="@+id/rvbParam2SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam2Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam3Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_3_name" />
+
+ <SeekBar android:id="@+id/rvbParam3SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam3Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam4Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_4_name" />
+
+ <SeekBar android:id="@+id/rvbParam4SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam4Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam5Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_5_name" />
+
+ <SeekBar android:id="@+id/rvbParam5SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam5Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam6Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_6_name" />
+
+ <SeekBar android:id="@+id/rvbParam6SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam6Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam7Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_7_name" />
+
+ <SeekBar android:id="@+id/rvbParam7SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam7Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam8Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_8_name" />
+
+ <SeekBar android:id="@+id/rvbParam8SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam8Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam9Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_9_name" />
+
+ <SeekBar android:id="@+id/rvbParam9SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam9Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam10Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_10_name" />
+
+ <SeekBar android:id="@+id/rvbParam10SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam10Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ </ScrollView>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/equalizertest.xml b/media/tests/EffectsTest/res/layout/equalizertest.xml
new file mode 100755
index 0000000..2223c48
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/equalizertest.xml
@@ -0,0 +1,468 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/eqReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/eqReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/eqControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/equalizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_1_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam1Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam1Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam1Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam2Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_2_name" />
+
+ <LinearLayout android:id="@+id/eqParam2Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam2Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam2Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam2Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam2SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam2Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam3Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_3_name" />
+
+ <LinearLayout android:id="@+id/eqParam3Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam3Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam3Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam3Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam3SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam3Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam4Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_4_name" />
+
+ <LinearLayout android:id="@+id/eqParam4Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam4Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam4Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam4Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam4SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam4Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam5Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_5_name" />
+
+ <LinearLayout android:id="@+id/eqParam5Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam5Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam5Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam5Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam5SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam5Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam6Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_6_name" />
+
+ <SeekBar android:id="@+id/eqParam6SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam6Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/presetreverbtest.xml b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
new file mode 100755
index 0000000..b648899
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/presetrvbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/presetrvbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/presetrvbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/presetrvbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/presetrvbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/presetrvbOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/presetrvbParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/presetrvb_param_1_name" />
+
+ <SeekBar android:id="@+id/presetrvbParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/presetrvbParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/virtualizertest.xml b/media/tests/EffectsTest/res/layout/virtualizertest.xml
new file mode 100755
index 0000000..c9203de
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/virtualizertest.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/virtReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/virtReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/virtControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/virtualizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/virtStrengthName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/stength_name" />
+
+ <LinearLayout android:id="@+id/virtStrengthDesc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="30dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtStrengthMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="left"
+ android:gravity="left"/>
+ <TextView android:id="@+id/virtStrengthMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:gravity="right"/>
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/virtStrengthSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/virtStrengthValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
new file mode 100755
index 0000000..8611e8c
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visuReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visualizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuCallbackLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuCallbackText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/visu_callback"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visuCallbackOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/waveformName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/waveform_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/waveformMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/waveformCenter"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/waveformMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/fftName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/fft_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/fftMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/fftCenter"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/fftMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/raw/mp3_sample.mp3 b/media/tests/EffectsTest/res/raw/mp3_sample.mp3
new file mode 100644
index 0000000..a9d8635
--- /dev/null
+++ b/media/tests/EffectsTest/res/raw/mp3_sample.mp3
Binary files differ
diff --git a/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav b/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav
new file mode 100644
index 0000000..2538b4d6
--- /dev/null
+++ b/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav
Binary files differ
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
new file mode 100755
index 0000000..2a85184
--- /dev/null
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Effects Test</string>
+ <string name="effect_control">Effect State</string>
+ <string name="effect_release">Effect Instantiated</string>
+ <string name="effect_bypass">Bypass</string>
+ <string name="envreverb_test_name">Environmental Reverb Test</string>
+ <string name="rvb_param_1_name">Room Level</string>
+ <string name="rvb_param_2_name">Room HF Level</string>
+ <string name="rvb_param_3_name">Decay Time</string>
+ <string name="rvb_param_4_name">Decay HF Ratio</string>
+ <string name="rvb_param_5_name">Reflections Level</string>
+ <string name="rvb_param_6_name">Reflections Delay</string>
+ <string name="rvb_param_7_name">Reverb Level</string>
+ <string name="rvb_param_8_name">Reverb Delay</string>
+ <string name="rvb_param_9_name">Diffusion</string>
+ <string name="rvb_param_10_name">Density</string>
+ <string name="presetrvb_test_name">Preset Reverb Test</string>
+ <string name="presetrvb_param_1_name">Presets</string>
+ <string name="equalizer_test_name">Equalizer Test</string>
+ <string name="session">Audio Session</string>
+ <string name="eq_param_1_name">Band 1 Level</string>
+ <string name="eq_param_2_name">Band 2 Level</string>
+ <string name="eq_param_3_name">Band 3 Level</string>
+ <string name="eq_param_4_name">Band 4 Level</string>
+ <string name="eq_param_5_name">Band 5 Level</string>
+ <string name="eq_param_6_name">Presets</string>
+ <string name="virtualizer_test_name">Virtualizer Test</string>
+ <string name="stength_name">Strength</string>
+ <string name="bassboost_test_name">Bass Boost Test</string>
+ <string name="visualizer_test_name">Visualizer Test</string>
+ <string name="visu_callback">Callback Mode</string>
+ <string name="waveform_name">PCM capture</string>
+ <string name="fft_name">FFT Capture</string>
+ <string name="effect_attach_off">Attach</string>
+ <string name="effect_attach_on">Detach</string>
+ <string name="send_level_name">Send Level</string>
+</resources>
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
new file mode 100755
index 0000000..1a10d64
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.BassBoost;
+import android.media.audiofx.AudioEffect;
+
+public class BassBoostTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "BassBoostTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter mStrength;
+ private BassBoost mBassBoost = null;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, BassBoost> sInstances = new HashMap<Integer, BassBoost>(10);
+ String mSettings = "";
+
+ public BassBoostTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.bassboosttest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
+
+ getEffect(sSession);
+
+ if (mBassBoost != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+
+ textView = (TextView)findViewById(R.id.bbStrengthMin);
+ textView.setText("0");
+ textView = (TextView)findViewById(R.id.bbStrengthMax);
+ textView.setText("1000");
+ seekBar = (SeekBar)findViewById(R.id.bbStrengthSeekBar);
+ textView = (TextView)findViewById(R.id.bbStrengthValue);
+ mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mStrength);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ Log.d(TAG, "onKey() keyCode: "+keyCode+" event.getAction(): "+event.getAction());
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mBassBoost != null) {
+ mStrength.setEffect(mBassBoost);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.bassboostOnOff) {
+ if (mBassBoost != null) {
+ mBassBoost.setEnabled(isChecked);
+ mStrength.updateDisplay();
+ }
+ }
+ if (buttonView.getId() == R.id.bbReleaseButton) {
+ if (isChecked) {
+ if (mBassBoost == null) {
+ getEffect(sSession);
+ if (mBassBoost != null) {
+ mStrength.setEffect(mBassBoost);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ }
+ } else {
+ if (mBassBoost != null) {
+ mStrength.setEnabled(false);
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class BassBoostParam extends EffectParameter {
+ private BassBoost mBassBoost;
+
+ public BassBoostParam(BassBoost bassboost, int min, int max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "o/oo");
+
+ mBassBoost = bassboost;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mBassBoost != null) {
+ mBassBoost.setStrength(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mBassBoost != null) {
+ return new Integer(mBassBoost.getRoundedStrength());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object effect) {
+ mBassBoost = (BassBoost)effect;
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener, AudioEffect.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ int p = byteArrayToInt(param, 0);
+ short v = byteArrayToShort(value, 0);
+
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+p+" v: "+v);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mBassBoost = sInstances.get(session);
+ } else {
+ try{
+ mBassBoost = new BassBoost(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"BassBoost effect not supported");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"BassBoost cannot get strength supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"BassBoost library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"BassBoost effect not found");
+ }
+ sInstances.put(session, mBassBoost);
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mBassBoost != null) {
+ if (mSettings != "") {
+ mBassBoost.setProperties(new BassBoost.Settings(mSettings));
+ }
+ mBassBoost.setEnableStatusListener(mEffectListener);
+ mBassBoost.setControlStatusListener(mEffectListener);
+ mBassBoost.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mBassBoost.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mBassBoost != null) {
+ mSettings = mBassBoost.getProperties().toString();
+ mBassBoost.release();
+ Log.d(TAG,"BassBoost released");
+ mBassBoost = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java
new file mode 100755
index 0000000..95077e7
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import android.widget.SeekBar;
+
+
+abstract class EffectParameter implements SeekBar.OnSeekBarChangeListener {
+
+ private final static String TAG = "EffectParameter";
+
+ protected int mMin;
+ protected int mMax;
+ protected String mUnit;
+ protected SeekBar mSeekBar;
+ protected TextView mValueText;
+
+ public EffectParameter (int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ mMin = min;
+ mMax = max;
+ mSeekBar = seekBar;
+ mValueText = textView;
+ mUnit = unit;
+ byte[] paramBuf = new byte[4];
+
+ mSeekBar.setMax(max-min);
+ }
+
+ public void displayValue(int value, boolean fromTouch) {
+ String text = Integer.toString(value)+" "+mUnit;
+ mValueText.setText(text);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ }
+ }
+
+ public void updateDisplay() {
+ displayValue(getParameter(), false);
+ }
+
+ public abstract void setParameter(Integer value);
+
+ public abstract Integer getParameter();
+
+ public abstract void setEffect(Object effect);
+
+ // SeekBar.OnSeekBarChangeListener
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+
+ if (seekBar != mSeekBar) {
+ Log.e(TAG, "onProgressChanged called with wrong seekBar");
+ return;
+ }
+
+ int value = progress + mMin;
+ if (fromTouch) {
+ setParameter(value);
+ }
+
+ displayValue(getParameter(), fromTouch);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void setEnabled(boolean e) {
+ mSeekBar.setEnabled(e);
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
new file mode 100755
index 0000000..6612766
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.media.audiofx.AudioEffect;
+import java.util.UUID;
+
+public class EffectsTest extends Activity {
+
+ private final static String TAG = "EffectsTest";
+
+
+ public EffectsTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.effectstest);
+
+ Button button = (Button) findViewById(R.id.env_reverb_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, EnvReverbTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.preset_reverb_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, PresetReverbTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.equalizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, EqualizerTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.virtualizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, VirtualizerTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.bassboost_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, BassBoostTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.visualizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, VisualizerTest.class));
+ }
+ });
+
+ AudioEffect.Descriptor[] descriptors = AudioEffect.queryEffects();
+
+ ListView list = (ListView) findViewById(R.id.effect_list);
+ list.setAdapter(new EffectListAdapter(this, descriptors));
+
+ }
+
+ private class EffectListAdapter extends BaseAdapter {
+
+ private Context mContext;
+
+ AudioEffect.Descriptor[] mDescriptors;
+
+ public EffectListAdapter(Context context, AudioEffect.Descriptor[] descriptors) {
+ Log.d(TAG, "EffectListAdapter contructor");
+ mContext = context;
+ mDescriptors = descriptors;
+ for (int i = 0; i < mDescriptors.length; i++) {
+ Log.d(TAG, "Effect: "+i+" name: "+ mDescriptors[i].name);
+ }
+ }
+
+ public int getCount() {
+ Log.d(TAG, "EffectListAdapter getCount(): "+mDescriptors.length);
+ return mDescriptors.length;
+ }
+
+ public Object getItem(int position) {
+ Log.d(TAG, "EffectListAdapter getItem() at: "+position+" name: "
+ +mDescriptors[position].name);
+ return mDescriptors[position];
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ EffectView ev;
+ if (convertView == null) {
+ Log.d(TAG, "getView() new EffectView position: " + position);
+ ev = new EffectView(mContext, mDescriptors);
+ } else {
+ Log.d(TAG, "getView() convertView position: " + position);
+ ev = new EffectView(mContext, mDescriptors);
+ //ev = (EffectView) convertView;
+ }
+ ev.set(position);
+ return ev;
+ }
+ }
+
+ private class EffectView extends LinearLayout {
+ private Context mContext;
+ AudioEffect.Descriptor[] mDescriptors;
+
+ public EffectView(Context context, AudioEffect.Descriptor[] descriptors) {
+ super(context);
+
+ mContext = context;
+ mDescriptors = descriptors;
+ this.setOrientation(VERTICAL);
+ }
+
+ public void set(int position) {
+ TextView tv = new TextView(mContext);
+ tv.setText("Effect "+ position);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" type: "+ mDescriptors[position].type.toString());
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" uuid: "+ mDescriptors[position].uuid.toString());
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" name: "+ mDescriptors[position].name);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" vendor: "+ mDescriptors[position].implementor);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" mode: "+ mDescriptors[position].connectMode);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ }
+ }
+
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java
new file mode 100755
index 0000000..594e844
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioManager;
+
+public class EnvReverbTest extends Activity implements OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
+
+ private final static String TAG = "EnvReverbTest";
+
+ private static int NUM_PARAMS = 10;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private EnvironmentalReverb mReverb;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ ToggleButton mAttachButton;
+ private static HashMap<Integer, EnvironmentalReverb> sInstances = new HashMap<Integer, EnvironmentalReverb>(10);
+ static SimplePlayer sPlayerController = null;
+ SeekBar mSendLevelSeekBar;
+ TextView mSendLevelDisplay;
+ static float sSendLevel = linToExp(50,100);
+ static boolean sAttached = false;
+ String mSettings = "";
+
+ public EnvReverbTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Log.d(TAG, "onCreate");
+ SeekBar seekBar;
+ TextView textView;
+ ToggleButton button;
+ setContentView(R.layout.envreverbtest);
+
+ ImageView playPause = (ImageView) findViewById(R.id.playPause1);
+ ImageView stop = (ImageView) findViewById(R.id.stop1);
+ textView = (TextView) findViewById(R.id.sessionText);
+ if (sPlayerController == null) {
+ sPlayerController = new SimplePlayer(this, R.id.playPause1, playPause,
+ R.id.stop1, stop, textView,
+ R.raw.mp3_sample, AudioManager.STREAM_MUSIC, 0);
+ } else {
+ sPlayerController.set(this, R.id.playPause1, playPause,
+ R.id.stop1, stop, textView,
+ AudioManager.STREAM_MUSIC, 0);
+ }
+
+ // send level
+ mSendLevelSeekBar = (SeekBar)findViewById(R.id.sendLevelSeekBar);
+ mSendLevelDisplay = (TextView)findViewById(R.id.sendLevelValue);
+ mSendLevelSeekBar.setMax(100);
+ mSendLevelSeekBar.setOnSeekBarChangeListener(this);
+ mSendLevelSeekBar.setProgress(expToLin(sSendLevel,100));
+ sPlayerController.setAuxEffectSendLevel(sSendLevel);
+
+ mOnOffButton = (ToggleButton)findViewById(R.id.rvbOnOff);
+ mReleaseButton = (ToggleButton)findViewById(R.id.rvbReleaseButton);
+ mAttachButton = (ToggleButton)findViewById(R.id.attachButton);
+
+ getEffect(0);
+
+ if (mReverb != null) {
+ mOnOffButton.setOnCheckedChangeListener(this);
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mAttachButton.setOnCheckedChangeListener(this);
+
+// button = (ToggleButton)findViewById(R.id.rvbBypass);
+// button.setChecked(false);
+// button.setOnCheckedChangeListener(this);
+
+ // Room level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam1SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam1Value);
+ mParameters[0] = new RoomLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+
+ // Room HF level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam2SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam2Value);
+ mParameters[1] = new RoomHFLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[1]);
+
+ // Decay time
+ seekBar = (SeekBar)findViewById(R.id.rvbParam3SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam3Value);
+ mParameters[2] = new DecayTimeParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[2]);
+
+ // Decay HF ratio
+ seekBar = (SeekBar)findViewById(R.id.rvbParam4SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam4Value);
+ mParameters[3] = new DecayHFRatioParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[3]);
+
+ // Reflections level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam5SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam5Value);
+ mParameters[4] = new ReflectionsLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[4]);
+
+ // Reflections delay
+ seekBar = (SeekBar)findViewById(R.id.rvbParam6SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam6Value);
+ mParameters[5] = new ReflectionsDelayParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[5]);
+
+ // Reverb level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam7SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam7Value);
+ mParameters[6] = new ReverbLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[6]);
+
+ // Reverb delay
+ seekBar = (SeekBar)findViewById(R.id.rvbParam8SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam8Value);
+ mParameters[7] = new ReverbDelayParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[7]);
+
+ // Diffusion
+ seekBar = (SeekBar)findViewById(R.id.rvbParam9SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam9Value);
+ mParameters[8] = new DiffusionParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[8]);
+
+ // Density
+ seekBar = (SeekBar)findViewById(R.id.rvbParam10SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam10Value);
+ mParameters[9] = new DensityParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[9]);
+ }
+ }
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.rvbOnOff) {
+ if (mReverb != null) {
+ mReverb.setEnabled(isChecked);
+ Log.d(TAG,"onCheckedChanged: rvbOnOff");
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.rvbReleaseButton) {
+ if (isChecked) {
+ if (mReverb == null) {
+ getEffect(0);
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mReverb);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ } else {
+ if (mReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(0);
+ }
+ }
+ }
+// if (buttonView.getId() == R.id.rvbBypass) {
+// // REVERB_PARAM_BYPASS parametervalue is 11 in EffectEnvironmentalReverApi.h
+// if (mReverb != null) {
+// if (isChecked) {
+// mReverb.setParameter((int)11, (int)1);
+// } else {
+// mReverb.setParameter((int)11, (int)0);
+// }
+// }
+// }
+ if (buttonView.getId() == R.id.attachButton) {
+ if (mReverb != null) {
+ if (isChecked) {
+ sPlayerController.attachAuxEffect(mReverb.getId());
+ sAttached = true;
+ } else {
+ sPlayerController.attachAuxEffect(0);
+ sAttached = false;
+ }
+ }
+ }
+ }
+
+ // SeekBar.OnSeekBarChangeListener
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+
+ if (seekBar != mSendLevelSeekBar) {
+ Log.e(TAG, "onProgressChanged called with wrong seekBar");
+ return;
+ }
+
+ sSendLevel = linToExp(progress,100);
+ if (fromTouch) {
+ sPlayerController.setAuxEffectSendLevel(sSendLevel);
+ }
+ String text = Float.toString(sSendLevel);
+ mSendLevelDisplay.setText(text);
+ if (!fromTouch) {
+ seekBar.setProgress(progress);
+ }
+ }
+
+ static float linToExp(int lin, int range) {
+ if (lin == 0) return 0;
+ return (float)Math.pow((double)10,(double)72*(lin-range)/(20*range));
+ }
+
+ static int expToLin(float exp, int range) {
+ if (exp == 0) return 0;
+ return (int)(20*range*Math.log10((double)exp)/72 + range);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ private class EnvReverbParam extends EffectParameter {
+ private EnvironmentalReverb mReverb;
+
+ public EnvReverbParam(EnvironmentalReverb reverb, int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ super (min, max, seekBar, textView, unit);
+ mReverb = reverb;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ }
+
+ @Override
+ public Integer getParameter() {
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object reverb) {
+ mReverb = (EnvironmentalReverb)reverb;
+ }
+ }
+
+ private class RoomLevelParam extends EnvReverbParam {
+
+ public RoomLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setRoomLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getRoomLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class RoomHFLevelParam extends EnvReverbParam {
+
+ public RoomHFLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -4000, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setRoomHFLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getRoomHFLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DecayTimeParam extends EnvReverbParam {
+
+ public DecayTimeParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 200, 4000, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDecayTime(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getDecayTime();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DecayHFRatioParam extends EnvReverbParam {
+
+ public DecayHFRatioParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 100, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDecayHFRatio(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDecayHFRatio());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReflectionsLevelParam extends EnvReverbParam {
+
+ public ReflectionsLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReflectionsLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getReflectionsLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReflectionsDelayParam extends EnvReverbParam {
+
+ public ReflectionsDelayParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 65, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReflectionsDelay(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getReflectionsDelay();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReverbLevelParam extends EnvReverbParam {
+
+ public ReverbLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 2000, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReverbLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getReverbLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReverbDelayParam extends EnvReverbParam {
+
+ public ReverbDelayParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 65, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReverbDelay(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getReverbDelay();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DiffusionParam extends EnvReverbParam {
+
+ public DiffusionParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDiffusion(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDiffusion());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DensityParam extends EnvReverbParam {
+
+ public DensityParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDensity(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDensity());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mReverb = sInstances.get(session);
+ } else {
+ try{
+ mReverb = new EnvironmentalReverb(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Reverb effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Reverb library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Reverb effect not found");
+ }
+ Log.d(TAG, "new reverb: "+mReverb);
+ sInstances.put(session, mReverb);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ mAttachButton.setEnabled(false);
+ if (mReverb != null) {
+ if (mSettings != "") {
+ mReverb.setProperties(new EnvironmentalReverb.Settings(mSettings));
+ }
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+ mOnOffButton.setChecked(mReverb.getEnabled());
+ mOnOffButton.setEnabled(true);
+ mAttachButton.setChecked(false);
+ mAttachButton.setEnabled(true);
+ if (sAttached) {
+ mAttachButton.setChecked(true);
+ sPlayerController.attachAuxEffect(mReverb.getId());
+ }
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ mAttachButton.setChecked(false);
+ mAttachButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mReverb != null) {
+ mSettings = mReverb.getProperties().toString();
+ mReverb.release();
+ Log.d(TAG,"Reverb released, settings: "+mSettings);
+ mReverb = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java
new file mode 100755
index 0000000..f30a26f
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import android.media.audiofx.Equalizer;
+import android.media.audiofx.AudioEffect;
+
+public class EqualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "EqualizerTest";
+
+ private static int NUM_BANDS = 5;
+ private static int NUM_PARAMS = NUM_BANDS + 1;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private Equalizer mEqualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, Equalizer> sInstances = new HashMap<Integer, Equalizer>(10);
+ String mSettings = "";
+
+ public EqualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.equalizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.eqReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.equalizerOnOff);
+
+ getEffect(sSession);
+
+ if (mEqualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+
+ short[] bandLevelRange = mEqualizer.getBandLevelRange();
+ int centerFreq;
+ int []freqRange;
+
+ // Band 1 level
+ centerFreq = mEqualizer.getCenterFreq((short)0);
+ freqRange = mEqualizer.getBandFreqRange((short)0);
+ displayFreq(R.id.eqParam1Center, centerFreq);
+ displayFreq(R.id.eqParam1Min, freqRange[0]);
+ displayFreq(R.id.eqParam1Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam1SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam1Value);
+ mParameters[0] = new BandLevelParam(mEqualizer, 0, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+
+ // Band 2 level
+ centerFreq = mEqualizer.getCenterFreq((short)1);
+ freqRange = mEqualizer.getBandFreqRange((short)1);
+ displayFreq(R.id.eqParam2Center, centerFreq);
+ displayFreq(R.id.eqParam2Min, freqRange[0]);
+ displayFreq(R.id.eqParam2Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam2SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam2Value);
+ mParameters[1] = new BandLevelParam(mEqualizer, 1, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[1]);
+
+ // Band 3 level
+ centerFreq = mEqualizer.getCenterFreq((short)2);
+ freqRange = mEqualizer.getBandFreqRange((short)2);
+ displayFreq(R.id.eqParam3Center, centerFreq);
+ displayFreq(R.id.eqParam3Min, freqRange[0]);
+ displayFreq(R.id.eqParam3Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam3SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam3Value);
+ mParameters[2] = new BandLevelParam(mEqualizer, 2, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[2]);
+
+ // Band 4 level
+ centerFreq = mEqualizer.getCenterFreq((short)3);
+ freqRange = mEqualizer.getBandFreqRange((short)3);
+ displayFreq(R.id.eqParam4Center, centerFreq);
+ displayFreq(R.id.eqParam4Min, freqRange[0]);
+ displayFreq(R.id.eqParam4Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam4SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam4Value);
+ mParameters[3] = new BandLevelParam(mEqualizer, 3, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[3]);
+
+ // Band 5 level
+ centerFreq = mEqualizer.getCenterFreq((short)4);
+ freqRange = mEqualizer.getBandFreqRange((short)4);
+ displayFreq(R.id.eqParam5Center, centerFreq);
+ displayFreq(R.id.eqParam5Min, freqRange[0]);
+ displayFreq(R.id.eqParam5Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam5SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam5Value);
+ mParameters[4] = new BandLevelParam(mEqualizer, 4, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[4]);
+
+ // Presets
+ short numPresets = mEqualizer.getNumberOfPresets();
+ seekBar = (SeekBar)findViewById(R.id.eqParam6SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam6Value);
+ mParameters[5] = new PresetParam(mEqualizer, (short)0, (short)(numPresets-1), seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[5]);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mEqualizer);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.equalizerOnOff) {
+ if (mEqualizer != null) {
+ mEqualizer.setEnabled(isChecked);
+ updateBands();
+ }
+ }
+ if (buttonView.getId() == R.id.eqReleaseButton) {
+ if (isChecked) {
+ if (mEqualizer == null) {
+ getEffect(sSession);
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mEqualizer);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ }
+ } else {
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ protected void updateBands() {
+ for (int i = 0 ; i < NUM_BANDS; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+
+ private void displayFreq(int viewId, int freq) {
+ TextView textView = (TextView)findViewById(viewId);
+ String text = Integer.toString(freq/1000)+" Hz";
+ textView.setText(text);
+ }
+
+ private class EqualizerParam extends EffectParameter {
+ private Equalizer mEqualizer;
+
+ public EqualizerParam(Equalizer equalizer, int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ super (min, max, seekBar, textView, unit);
+
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ }
+
+ @Override
+ public Integer getParameter() {
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object eq) {
+ mEqualizer = (Equalizer)eq;
+ }
+ }
+
+ private class BandLevelParam extends EqualizerParam {
+ private int mBand;
+
+ public BandLevelParam(Equalizer equalizer, int band, short min, short max, SeekBar seekBar, TextView textView) {
+ super (equalizer, min, max, seekBar, textView, "mB");
+
+ mBand = band;
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mEqualizer != null) {
+ mEqualizer.setBandLevel((short)mBand, value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mEqualizer != null) {
+ return new Integer(mEqualizer.getBandLevel((short)mBand));
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class PresetParam extends EqualizerParam {
+
+ public PresetParam(Equalizer equalizer, short min, short max, SeekBar seekBar, TextView textView) {
+ super (equalizer, min, max, seekBar, textView, "");
+
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mEqualizer != null) {
+ mEqualizer.usePreset(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mEqualizer != null) {
+ return new Integer(mEqualizer.getCurrentPreset());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void displayValue(int value, boolean fromTouch) {
+ String text = mEqualizer.getPresetName((short)value);
+ mValueText.setText(text);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ } else {
+ updateBands();
+ }
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener,
+ Equalizer.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+
+ public void onParameterChange(Equalizer effect, int status, int param1, int param2, int value) {
+ Log.d(TAG,"onParameterChange EQ, status: "+status+" p1: "+param1+" p2: "+param2+" v: "+value);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mEqualizer = sInstances.get(session);
+ } else {
+ try{
+ mEqualizer = new Equalizer(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Equalizer effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Equalizer library not loaded");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"Equalizer cannot get presets");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Equalizer effect not found");
+ }
+ sInstances.put(session, mEqualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ if (mEqualizer != null) {
+ if (mSettings != "") {
+ Log.d(TAG,"Equalizer settings: "+mSettings);
+ mEqualizer.setProperties(new Equalizer.Settings(mSettings));
+ }
+
+ mEqualizer.setEnableStatusListener(mEffectListener);
+ mEqualizer.setControlStatusListener(mEffectListener);
+ mEqualizer.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mEqualizer.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+// mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mEqualizer != null) {
+ mSettings = mEqualizer.getProperties().toString();
+ mEqualizer.release();
+ Log.d(TAG,"Equalizer released, settings: "+mSettings);
+ mEqualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java b/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java
new file mode 100755
index 0000000..91d7948
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.PresetReverb;
+import android.media.audiofx.AudioEffect;
+
+public class PresetReverbTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "PresetReverbTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private PresetReverb mPresetReverb;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, PresetReverb> sInstances = new HashMap<Integer, PresetReverb>(10);
+ String mSettings = "";
+
+ public PresetReverbTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ private static String[] sPresetNames = {
+ "NONE", //PresetReverb.PRESET_NONE
+ "SMALLROOM", //PresetReverb.PRESET_SMALLROOM
+ "MEDIUMROOM", //PresetReverb.PRESET_MEDIUMROOM
+ "LARGEROOM", //PresetReverb.PRESET_LARGEROOM
+ "MEDIUMHALL", //PresetReverb.PRESET_MEDIUMHALL
+ "LARGEHALL", //PresetReverb.PRESET_LARGEHALL
+ "PLATE", //PresetReverb.PRESET_PLATE
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.presetreverbtest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.presetrvbReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.presetrvbOnOff);
+
+ getEffect(sSession);
+
+ if (mPresetReverb != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ // Presets
+ SeekBar seekBar = (SeekBar)findViewById(R.id.presetrvbParam1SeekBar);
+ TextView textView = (TextView)findViewById(R.id.presetrvbParam1Value);
+ mParameters[0] = new PresetParam(mPresetReverb, (short)0, (short)(sPresetNames.length - 1), seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mPresetReverb);
+ mParameters[i].setEnabled(true);
+ mParameters[i].updateDisplay();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.presetrvbOnOff) {
+ if (mPresetReverb != null) {
+ mPresetReverb.setEnabled(isChecked);
+ updateParams();
+ }
+ }
+ if (buttonView.getId() == R.id.presetrvbReleaseButton) {
+ if (isChecked) {
+ if (mPresetReverb == null) {
+ getEffect(sSession);
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mPresetReverb);
+ mParameters[i].setEnabled(true);
+ mParameters[i].updateDisplay();
+ }
+ }
+ }
+ } else {
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class PresetParam extends EffectParameter {
+ private PresetReverb mPresetReverb;
+
+ public PresetParam(PresetReverb presetrvb, short min, short max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "");
+
+ mPresetReverb = presetrvb;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mPresetReverb != null) {
+ mPresetReverb.setPreset(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mPresetReverb != null) {
+ return new Integer(mPresetReverb.getPreset());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void displayValue(int value, boolean fromTouch) {
+ mValueText.setText(sPresetNames[value]);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ } else {
+ updateParams();
+ }
+ }
+
+ @Override
+ public void setEffect(Object presetrvb) {
+ mPresetReverb = (PresetReverb)presetrvb;
+ }
+
+ }
+
+ protected void updateParams() {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener,
+ PresetReverb.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+
+ public void onParameterChange(PresetReverb effect, int status, int param, short value) {
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+param+" v: "+value);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mPresetReverb = sInstances.get(session);
+ } else {
+ try{
+ mPresetReverb = new PresetReverb(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"PresetReverb effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"PresetReverb library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"PresetReverb effect not found");
+ }
+ sInstances.put(session, mPresetReverb);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mPresetReverb != null) {
+ if (mSettings != "") {
+ mPresetReverb.setProperties(new PresetReverb.Settings(mSettings));
+ }
+ mPresetReverb.setEnableStatusListener(mEffectListener);
+ mPresetReverb.setControlStatusListener(mEffectListener);
+ mPresetReverb.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mPresetReverb.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mPresetReverb != null) {
+ mSettings = mPresetReverb.getProperties().toString();
+ mPresetReverb.release();
+ Log.d(TAG,"PresetReverb released");
+ mPresetReverb = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java b/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java
new file mode 100644
index 0000000..119a604
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SimplePlayer implements OnClickListener {
+
+ private final static String TAG = "SimplePlayer";
+
+ int mPlayPauseButtonId;
+ int mStopButtonId;
+ Context mContext;
+ ImageView mPlayPauseButton;
+ int mPlayImageResource;
+ int mPauseImageResource;
+ String mFileName;
+ int mFileResId;
+ MediaPlayer mMediaPlayer;
+ int mStreamType;
+ int mSession;
+ float mSendLevel = (float)0.5;
+ int mEffectId = 0;
+ TextView mSessionText;
+
+ SimplePlayer(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, String fileName, int stream, int session)
+ {
+ set(context, playPausebuttonId, playPausebutton, stopButtonId, stopButton, sessionText, stream, session);
+ mFileName = fileName;
+ }
+
+ SimplePlayer(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, int fileResId, int stream, int session) {
+ set(context, playPausebuttonId, playPausebutton, stopButtonId, stopButton, sessionText, stream, session);
+ mFileResId = fileResId;
+ mFileName = "";
+ }
+
+ public void set(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, int stream, int session) {
+ mContext = context;
+ mPlayPauseButtonId = playPausebuttonId;
+ mStopButtonId = stopButtonId;
+ mPlayPauseButton = (ImageButton) playPausebutton;
+ ImageButton stop = (ImageButton) stopButton;
+
+ mPlayPauseButton.setOnClickListener(this);
+ mPlayPauseButton.requestFocus();
+ stop.setOnClickListener(this);
+
+ mPlayImageResource = android.R.drawable.ic_media_play;
+ mPauseImageResource = android.R.drawable.ic_media_pause;
+ mStreamType = stream;
+ mSession = session;
+ mSessionText = sessionText;
+ }
+
+
+ public void onClick(View v) {
+ if (v.getId() == mPlayPauseButtonId) {
+ playOrPause();
+ } else if (v.getId() == mStopButtonId) {
+ stop();
+ }
+ }
+
+ public void playOrPause() {
+ if (mMediaPlayer == null || !mMediaPlayer.isPlaying()){
+ if (mMediaPlayer == null) {
+ try {
+ mMediaPlayer = new MediaPlayer();
+ if (mSession != 0) {
+ mMediaPlayer.setAudioSessionId(mSession);
+ Log.d(TAG, "mMediaPlayer.setAudioSessionId(): "+ mSession);
+ }
+
+ if (mFileName.equals("")) {
+ Log.d(TAG, "Playing from resource");
+ AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(mFileResId);
+ mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ afd.close();
+ } else {
+ Log.d(TAG, "Playing file: "+mFileName);
+ mMediaPlayer.setDataSource(mFileName);
+ }
+ mMediaPlayer.setAudioStreamType(mStreamType);
+ mMediaPlayer.prepare();
+ mMediaPlayer.setLooping(true);
+ } catch (IOException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ } catch (IllegalArgumentException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ } catch (SecurityException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ }
+
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setAuxEffectSendLevel(mSendLevel);
+ mMediaPlayer.attachAuxEffect(mEffectId);
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ updatePlayPauseButton();
+ }
+ });
+ mSessionText.setText("Session: "+Integer.toString(mMediaPlayer.getAudioSessionId()));
+ }
+ }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.start();
+ }
+ } else {
+ mMediaPlayer.pause();
+ }
+ updatePlayPauseButton();
+ }
+
+ public void stop() {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.stop();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+ updatePlayPauseButton();
+ }
+
+ public boolean isPlaying() {
+ if (mMediaPlayer != null) {
+ return mMediaPlayer.isPlaying();
+ } else {
+ return false;
+ }
+ }
+
+ public void updatePlayPauseButton() {
+ mPlayPauseButton.setImageResource(isPlaying() ? mPauseImageResource : mPlayImageResource);
+ }
+
+ public void attachAuxEffect(int effectId) {
+ mEffectId = effectId;
+ if (mMediaPlayer != null) {
+ Log.d(TAG,"attach effect: "+effectId);
+ mMediaPlayer.attachAuxEffect(effectId);
+ }
+ }
+ public void setAuxEffectSendLevel(float level) {
+ mSendLevel = level;
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setAuxEffectSendLevel(level);
+ }
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java
new file mode 100755
index 0000000..bb32e6f
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.Virtualizer;
+import android.media.audiofx.AudioEffect;
+
+public class VirtualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "VirtualizerTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter mStrength;
+ private Virtualizer mVirtualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, Virtualizer> sInstances = new HashMap<Integer, Virtualizer>(10);
+ String mSettings = "";
+
+ public VirtualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.virtualizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.virtReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.virtualizerOnOff);
+
+ getEffect(sSession);
+
+ if (mVirtualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ textView = (TextView)findViewById(R.id.virtStrengthMin);
+ textView.setText("0");
+ textView = (TextView)findViewById(R.id.virtStrengthMax);
+ textView.setText("1000");
+ seekBar = (SeekBar)findViewById(R.id.virtStrengthSeekBar);
+ textView = (TextView)findViewById(R.id.virtStrengthValue);
+ mStrength = new VirtualizerParam(mVirtualizer, 0, 1000, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mStrength);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mVirtualizer != null) {
+ mStrength.setEffect(mVirtualizer);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.virtualizerOnOff) {
+ if (mVirtualizer != null) {
+ mVirtualizer.setEnabled(isChecked);
+ mStrength.updateDisplay();
+ }
+ }
+ if (buttonView.getId() == R.id.virtReleaseButton) {
+ if (isChecked) {
+ if (mVirtualizer == null) {
+ getEffect(sSession);
+ if (mVirtualizer != null) {
+ mStrength.setEffect(mVirtualizer);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ }
+ } else {
+ if (mVirtualizer != null) {
+ mStrength.setEnabled(false);
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class VirtualizerParam extends EffectParameter {
+ private Virtualizer mVirtualizer;
+
+ public VirtualizerParam(Virtualizer virtualizer, int min, int max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "o/oo");
+
+ mVirtualizer = virtualizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mVirtualizer != null) {
+ mVirtualizer.setStrength(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mVirtualizer != null) {
+ return new Integer(mVirtualizer.getRoundedStrength());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object effect) {
+ mVirtualizer = (Virtualizer)effect;
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener, AudioEffect.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ int p = byteArrayToInt(param, 0);
+ short v = byteArrayToShort(value, 0);
+
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+p+" v: "+v);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mVirtualizer = sInstances.get(session);
+ } else {
+ try{
+ mVirtualizer = new Virtualizer(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Virtualizer effect not supported");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"Virtualizer cannot get strength supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Virtualizer library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Virtualizer effect not found");
+ }
+ sInstances.put(session, mVirtualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mVirtualizer != null) {
+ if (mSettings != "") {
+ mVirtualizer.setProperties(new Virtualizer.Settings(mSettings));
+ }
+ mVirtualizer.setEnableStatusListener(mEffectListener);
+ mVirtualizer.setControlStatusListener(mEffectListener);
+ mVirtualizer.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mVirtualizer.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mVirtualizer != null) {
+ mSettings = mVirtualizer.getProperties().toString();
+ mVirtualizer.release();
+ Log.d(TAG,"Virtualizer released");
+ mVirtualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
new file mode 100755
index 0000000..60583e0
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 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.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.audiofx.Visualizer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+import android.widget.SeekBar;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VisualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "Visualizer Test";
+
+ private Visualizer mVisualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ boolean mEnabled;
+ EditText mSessionText;
+ static int sSession = 0;
+ int mCaptureSize;
+ ToggleButton mCallbackButton;
+ boolean mCallbackOn;
+ VisualizerListener mVisualizerListener;
+ private static HashMap<Integer, Visualizer> sInstances = new HashMap<Integer, Visualizer>(10);
+ private VisualizerTestHandler mVisualizerTestHandler = null;
+
+ public VisualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ TextView textView;
+
+ setContentView(R.layout.visualizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.visuReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.visualizerOnOff);
+ mCallbackButton = (ToggleButton)findViewById(R.id.visuCallbackOnOff);
+ mCallbackOn = false;
+ mCallbackButton.setChecked(mCallbackOn);
+
+ mVisualizerTestHandler = new VisualizerTestHandler();
+ mVisualizerListener = new VisualizerListener();
+
+ getEffect(sSession);
+
+ if (mVisualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ mCallbackButton.setOnCheckedChangeListener(this);
+ }
+ }
+
+ private static final int MSG_START_CAPTURE = 0;
+ private static final int MSG_STOP_CAPTURE = 1;
+ private static final int MSG_NEW_CAPTURE = 2;
+ private static final int CAPTURE_PERIOD_MS = 100;
+
+ private class VisualizerTestHandler extends Handler {
+ boolean mActive = false;
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_CAPTURE:
+ if (!mActive) {
+ Log.d(TAG, "Start capture");
+ mActive = true;
+ sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
+ }
+ break;
+ case MSG_STOP_CAPTURE:
+ if (mActive) {
+ Log.d(TAG, "Stop capture");
+ mActive = false;
+ }
+ break;
+ case MSG_NEW_CAPTURE:
+ if (mActive && mVisualizer != null) {
+ if (mCaptureSize > 0) {
+ byte[] data = new byte[mCaptureSize];
+ if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) {
+ int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+ displayVal(R.id.waveformMin, data[0]);
+ displayVal(R.id.waveformMax, data[len-1]);
+ displayVal(R.id.waveformCenter, data[len/2]);
+ };
+ if (mVisualizer.getFft(data) == Visualizer.SUCCESS) {
+ int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+ displayVal(R.id.fftMin, data[0]);
+ displayVal(R.id.fftMax, data[len-1]);
+ displayVal(R.id.fftCenter, data[len/2]);
+ };
+ }
+ sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
+ }
+ break;
+ }
+ }
+ }
+
+ private class VisualizerListener implements Visualizer.OnDataCaptureListener {
+
+ public VisualizerListener() {
+ }
+ public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
+ if (visualizer == mVisualizer) {
+ if (waveform.length > 0) {
+ Log.d(TAG, "onWaveFormDataCapture(): "+waveform[0]+" smp rate: "+samplingRate/1000);
+ displayVal(R.id.waveformMin, waveform[0]);
+ displayVal(R.id.waveformMax, waveform[waveform.length - 1]);
+ displayVal(R.id.waveformCenter, waveform[waveform.length/2]);
+ }
+ }
+ }
+ public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
+ if (visualizer == mVisualizer) {
+ if (fft.length > 0) {
+ Log.d(TAG, "onFftDataCapture(): "+fft[0]);
+ displayVal(R.id.fftMin, fft[0]);
+ displayVal(R.id.fftMax, fft[fft.length - 1]);
+ displayVal(R.id.fftCenter, fft[fft.length/2]);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.visualizerOnOff) {
+ if (mVisualizer != null) {
+ mEnabled = isChecked;
+ mCallbackButton.setEnabled(!mEnabled);
+ if (mCallbackOn && mEnabled) {
+ mVisualizer.setDataCaptureListener(mVisualizerListener,
+ 10000,
+ true,
+ true);
+ }
+ mVisualizer.setEnabled(mEnabled);
+ if (mCallbackOn) {
+ if (!mEnabled) {
+ mVisualizer.setDataCaptureListener(null,
+ 10000,
+ false,
+ false);
+ }
+ } else {
+ int msg = isChecked ? MSG_START_CAPTURE : MSG_STOP_CAPTURE;
+ mVisualizerTestHandler.sendMessage(
+ mVisualizerTestHandler.obtainMessage(msg, 0, 0, null));
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.visuReleaseButton) {
+ if (isChecked) {
+ if (mVisualizer == null) {
+ getEffect(sSession);
+ }
+ } else {
+ if (mVisualizer != null) {
+ putEffect(sSession);
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.visuCallbackOnOff) {
+ mCallbackOn = isChecked;
+ }
+ }
+
+ private void displayVal(int viewId, int val) {
+ TextView textView = (TextView)findViewById(viewId);
+ String text = Integer.toString(val);
+ textView.setText(text);
+ }
+
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mVisualizer = sInstances.get(session);
+ } else {
+ try{
+ mVisualizer = new Visualizer(session);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Visualizer library not loaded");
+ throw (new RuntimeException("Cannot initialize effect"));
+ } catch (RuntimeException e) {
+ throw e;
+ }
+ sInstances.put(session, mVisualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ if (mVisualizer != null) {
+ mCaptureSize = mVisualizer.getCaptureSize();
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mEnabled = mVisualizer.getEnabled();
+ mOnOffButton.setChecked(mEnabled);
+ mOnOffButton.setEnabled(true);
+
+ mCallbackButton.setEnabled(!mEnabled);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ Log.d(TAG,"Visualizer released");
+ mVisualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+
+}
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 7eb51dd..74cf80e 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -123,6 +123,11 @@
return config->smallestScreenWidthDp;
}
+int32_t AConfiguration_getLayoutDirection(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_LAYOUTDIR)
+ >> ResTable_config::SHIFT_LAYOUTDIR;
+}
+
// ----------------------------------------------------------------------
void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
@@ -210,6 +215,11 @@
config->smallestScreenWidthDp = value;
}
+void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ((value<<ResTable_config::SHIFT_LAYOUTDIR)&ResTable_config::MASK_LAYOUTDIR);
+}
+
// ----------------------------------------------------------------------
int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
index 99c0fd3..ca0c902 100644
--- a/native/android/native_window.cpp
+++ b/native/android/native_window.cpp
@@ -25,7 +25,7 @@
using namespace android;
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
- sp<ANativeWindow> win = android_Surface_getNativeWindow(env, surface);
+ sp<ANativeWindow> win = android_view_Surface_getNativeWindow(env, surface);
if (win != NULL) {
win->incStrong((void*)ANativeWindow_acquire);
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 9a80090..92261da 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -35,6 +35,7 @@
<bool name="def_bluetooth_on">false</bool>
<bool name="def_install_non_market_apps">false</bool>
+ <bool name="def_package_verifier_enable">true</bool>
<!-- Comma-separated list of location providers.
Network location is off by default because it requires
user opt-in via Setup Wizard or Settings.
@@ -46,6 +47,8 @@
<bool name="def_netstats_enabled">true</bool>
<bool name="def_usb_mass_storage_enabled">true</bool>
<bool name="def_wifi_on">false</bool>
+ <!-- 0 == never, 1 == only when plugged in, 2 == always -->
+ <integer name="def_wifi_sleep_policy">2</integer>
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
@@ -126,6 +129,15 @@
<!-- Default for Settings.Secure.TOUCH_EXPLORATION_ENABLED -->
<bool name="def_touch_exploration_enabled">false</bool>
+ <!-- Default value for Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE -->
+ <fraction name="def_accessibility_display_magnification_scale">200%</fraction>
+
+ <!-- Default value for Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED -->
+ <bool name="def_accessibility_display_magnification_enabled">false</bool>
+
+ <!-- Default value for Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE -->
+ <bool name="def_accessibility_display_magnification_auto_update">true</bool>
+
<!-- Default for Settings.System.USER_ROTATION -->
<integer name="def_user_rotation">0</integer>
@@ -158,7 +170,7 @@
<!-- Whether the feature activates when docked (SCREENSAVER_ACTIVATE_ON_DOCK) -->
<bool name="def_screensaver_activate_on_dock">true</bool>
<!-- Whether the feature activates when docked (SCREENSAVER_ACTIVATE_ON_SLEEP) -->
- <bool name="def_screensaver_activate_on_sleep">true</bool>
+ <bool name="def_screensaver_activate_on_sleep">false</bool>
<!-- ComponentName of the default screen saver (Settings.Secure.SCREENSAVER_COMPONENT) -->
<string name="def_screensaver_component">com.google.android.deskclock/com.android.deskclock.Screensaver</string>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index db81786..f153904 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -30,7 +30,9 @@
import android.media.AudioManager;
import android.media.AudioService;
import android.net.ConnectivityManager;
+import android.os.Environment;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
@@ -48,6 +50,7 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
@@ -64,15 +67,21 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 81;
+ private static final int DATABASE_VERSION = 85;
private Context mContext;
+ private int mUserHandle;
private static final HashSet<String> mValidTables = new HashSet<String>();
+ private static final String TABLE_SYSTEM = "system";
+ private static final String TABLE_SECURE = "secure";
+ private static final String TABLE_GLOBAL = "global";
+
static {
- mValidTables.add("system");
- mValidTables.add("secure");
+ mValidTables.add(TABLE_SYSTEM);
+ mValidTables.add(TABLE_SECURE);
+ mValidTables.add(TABLE_GLOBAL);
mValidTables.add("bluetooth_devices");
mValidTables.add("bookmarks");
@@ -82,9 +91,23 @@
mValidTables.add("old_favorites");
}
- public DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ static String dbNameForUser(final int userHandle) {
+ // The owner gets the unadorned db name;
+ if (userHandle == UserHandle.USER_OWNER) {
+ return DATABASE_NAME;
+ } else {
+ // Place the database in the user-specific data tree so that it's
+ // cleaned up automatically when the user is deleted.
+ File databaseFile = new File(
+ Environment.getUserSystemDirectory(userHandle), DATABASE_NAME);
+ return databaseFile.getPath();
+ }
+ }
+
+ public DatabaseHelper(Context context, int userHandle) {
+ super(context, dbNameForUser(userHandle), null, DATABASE_VERSION);
mContext = context;
+ mUserHandle = userHandle;
setWriteAheadLoggingEnabled(true);
}
@@ -101,6 +124,15 @@
db.execSQL("CREATE INDEX secureIndex1 ON secure (name);");
}
+ private void createGlobalTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE global (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "name TEXT UNIQUE ON CONFLICT REPLACE," +
+ "value TEXT" +
+ ");");
+ db.execSQL("CREATE INDEX globalIndex1 ON global (name);");
+ }
+
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE system (" +
@@ -112,6 +144,11 @@
createSecureTable(db);
+ // Only create the global table for the singleton 'owner' user
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ createGlobalTable(db);
+ }
+
db.execSQL("CREATE TABLE bluetooth_devices (" +
"_id INTEGER PRIMARY KEY," +
"name TEXT," +
@@ -271,7 +308,7 @@
Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS,
Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS,
};
- moveFromSystemToSecure(db, settingsToMove);
+ moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
upgradeVersion = 28;
}
@@ -637,7 +674,7 @@
"lockscreen.lockedoutpermanently",
"lockscreen.password_salt"
};
- moveFromSystemToSecure(db, settingsToMove);
+ moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
upgradeVersion = 52;
}
@@ -687,7 +724,7 @@
Secure.SET_INSTALL_LOCATION,
Secure.DEFAULT_INSTALL_LOCATION
};
- moveFromSystemToSecure(db, settingsToMove);
+ moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1013,7 +1050,7 @@
SQLiteStatement stmt = null;
Cursor c = null;
try {
- c = db.query("secure", new String[] {"_id", "value"},
+ c = db.query(TABLE_SECURE, new String[] {"_id", "value"},
"name='lockscreen.disabled'",
null, null, null, null);
// only set default if it has not yet been set
@@ -1089,14 +1126,14 @@
// toggle touch exploration. Note that the user has already manually
// enabled the services and touch exploration which means the she has
// given consent to have these services work in touch exploration mode.
- final boolean accessibilityEnabled = getIntValueFromTable(db, "secure",
+ final boolean accessibilityEnabled = getIntValueFromTable(db, TABLE_SECURE,
Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
- final boolean touchExplorationEnabled = getIntValueFromTable(db, "secure",
+ final boolean touchExplorationEnabled = getIntValueFromTable(db, TABLE_SECURE,
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
if (accessibilityEnabled && touchExplorationEnabled) {
- String enabledServices = getStringValueFromTable(db, "secure",
+ String enabledServices = getStringValueFromTable(db, TABLE_SECURE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
- String touchExplorationGrantedServices = getStringValueFromTable(db, "secure",
+ String touchExplorationGrantedServices = getStringValueFromTable(db, TABLE_SECURE,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, "");
if (TextUtils.isEmpty(touchExplorationGrantedServices)
&& !TextUtils.isEmpty(enabledServices)) {
@@ -1137,6 +1174,7 @@
R.string.def_screensaver_component);
loadStringSetting(stmt, Settings.Secure.SCREENSAVER_COMPONENTS,
R.string.def_screensaver_component);
+
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -1145,11 +1183,102 @@
upgradeVersion = 81;
}
+ if (upgradeVersion == 81) {
+ // Add package verification setting
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Settings.Secure.PACKAGE_VERIFIER_ENABLE,
+ R.bool.def_package_verifier_enable);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 82;
+ }
+
+ if (upgradeVersion == 82) {
+ // Move to per-user settings dbs
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ // Migrate now-global settings. Note that this happens before
+ // new users can be created.
+ createGlobalTable(db);
+ String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys);
+ moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove);
+ settingsToMove = hashsetToStringArray(SettingsProvider.sSecureGlobalKeys);
+ moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 83;
+ }
+
+ if (upgradeVersion == 83) {
+ // 1. Setting whether screen magnification is enabled.
+ // 2. Setting for screen magnification scale.
+ // 3. Setting for screen magnification auto update.
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT INTO secure(name,value) VALUES(?,?);");
+ loadBooleanSetting(stmt,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ R.bool.def_accessibility_display_magnification_enabled);
+ stmt.close();
+ stmt = db.compileStatement("INSERT INTO secure(name,value) VALUES(?,?);");
+ loadFractionSetting(stmt, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ R.fraction.def_accessibility_display_magnification_scale, 1);
+ stmt.close();
+ stmt = db.compileStatement("INSERT INTO secure(name,value) VALUES(?,?);");
+ loadBooleanSetting(stmt,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ R.bool.def_accessibility_display_magnification_auto_update);
+
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 84;
+ }
+
+ if (upgradeVersion == 84) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ // Patch up the slightly-wrong key migration from 82 -> 83
+ String[] settingsToMove = {
+ Settings.Secure.ADB_ENABLED,
+ Settings.Secure.BLUETOOTH_ON,
+ Settings.Secure.DATA_ROAMING,
+ Settings.Secure.DEVICE_PROVISIONED,
+ Settings.Secure.INSTALL_NON_MARKET_APPS,
+ Settings.Secure.USB_MASS_STORAGE_ENABLED
+ };
+ moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 85;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
+ ", must wipe the settings provider");
+ db.execSQL("DROP TABLE IF EXISTS global");
+ db.execSQL("DROP TABLE IF EXISTS globalIndex1");
db.execSQL("DROP TABLE IF EXISTS system");
db.execSQL("DROP INDEX IF EXISTS systemIndex1");
db.execSQL("DROP TABLE IF EXISTS secure");
@@ -1170,18 +1299,24 @@
}
}
- private void moveFromSystemToSecure(SQLiteDatabase db, String [] settingsToMove) {
- // Copy settings values from 'system' to 'secure' and delete them from 'system'
+ private String[] hashsetToStringArray(HashSet<String> set) {
+ String[] array = new String[set.size()];
+ return set.toArray(array);
+ }
+
+ private void moveSettingsToNewTable(SQLiteDatabase db,
+ String sourceTable, String destTable,
+ String[] settingsToMove) {
+ // Copy settings values from the source table to the dest, and remove from the source
SQLiteStatement insertStmt = null;
SQLiteStatement deleteStmt = null;
db.beginTransaction();
try {
- insertStmt =
- db.compileStatement("INSERT INTO secure (name,value) SELECT name,value FROM "
- + "system WHERE name=?");
- deleteStmt = db.compileStatement("DELETE FROM system WHERE name=?");
-
+ insertStmt = db.compileStatement("INSERT INTO "
+ + destTable + " (name,value) SELECT name,value FROM "
+ + sourceTable + " WHERE name=?");
+ deleteStmt = db.compileStatement("DELETE FROM " + sourceTable + " WHERE name=?");
for (String setting : settingsToMove) {
insertStmt.bindString(1, setting);
@@ -1203,7 +1338,7 @@
}
private void upgradeLockPatternLocation(SQLiteDatabase db) {
- Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'",
+ Cursor c = db.query(TABLE_SYSTEM, new String[] {"_id", "value"}, "name='lock_pattern'",
null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
@@ -1220,7 +1355,7 @@
}
}
c.close();
- db.delete("system", "name='lock_pattern'", null);
+ db.delete(TABLE_SYSTEM, "name='lock_pattern'", null);
} else {
c.close();
}
@@ -1228,7 +1363,7 @@
private void upgradeScreenTimeoutFromNever(SQLiteDatabase db) {
// See if the timeout is -1 (for "Never").
- Cursor c = db.query("system", new String[] { "_id", "value" }, "name=? AND value=?",
+ Cursor c = db.query(TABLE_SYSTEM, new String[] { "_id", "value" }, "name=? AND value=?",
new String[] { Settings.System.SCREEN_OFF_TIMEOUT, "-1" },
null, null, null);
@@ -1497,6 +1632,10 @@
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
loadSecureSettings(db);
+ // The global table only exists for the 'owner' user
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ loadGlobalSettings(db);
+ }
}
private void loadSystemSettings(SQLiteDatabase db) {
@@ -1507,10 +1646,6 @@
loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
R.bool.def_dim_screen);
- loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
- ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
- mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
- ? 1 : 0);
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
@@ -1529,21 +1664,6 @@
// Set default tty mode
loadSetting(stmt, Settings.System.TTY_MODE, 0);
- loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
- R.bool.def_airplane_mode_on);
-
- loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
- R.string.def_airplane_mode_radios);
-
- loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
- R.string.airplane_mode_toggleable_radios);
-
- loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
- R.bool.def_auto_time); // Sync time to NITZ
-
- loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
- R.bool.def_auto_time_zone); // Sync timezone to NITZ
-
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
@@ -1567,7 +1687,6 @@
loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
R.integer.def_pointer_speed);
-
} finally {
if (stmt != null) stmt.close();
}
@@ -1622,36 +1741,12 @@
stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON,
- R.bool.def_bluetooth_on);
-
- // Data roaming default, based on build
- loadSetting(stmt, Settings.Secure.DATA_ROAMING,
- "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.dataroaming",
- "false")) ? 1 : 0);
-
- // Mobile Data default, based on build
- loadSetting(stmt, Settings.Secure.MOBILE_DATA,
- "true".equalsIgnoreCase(
- SystemProperties.get("ro.com.android.mobiledata",
- "true")) ? 1 : 0);
-
- loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
- R.bool.def_install_non_market_apps);
+ loadBooleanSetting(stmt, Settings.Secure.PACKAGE_VERIFIER_ENABLE,
+ R.bool.def_package_verifier_enable);
loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
R.string.def_location_providers_allowed);
- loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
- R.bool.assisted_gps_enabled);
-
- loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
- R.integer.def_network_preference);
-
- loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED,
- R.bool.def_usb_mass_storage_enabled);
-
loadBooleanSetting(stmt, Settings.Secure.WIFI_ON,
R.bool.def_wifi_on);
loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
@@ -1672,10 +1767,6 @@
}
loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
- // Enable or disable Cell Broadcast SMS
- loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS,
- RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
-
// Don't do this. The SystemServer will initialize ADB_ENABLED from a
// persistent system property instead.
//loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0);
@@ -1704,20 +1795,6 @@
loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
R.string.def_accessibility_web_content_key_bindings);
- final int maxBytes = mContext.getResources().getInteger(
- R.integer.def_download_manager_max_bytes_over_mobile);
- if (maxBytes > 0) {
- loadSetting(stmt, Settings.Secure.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
- Integer.toString(maxBytes));
- }
-
- final int recommendedMaxBytes = mContext.getResources().getInteger(
- R.integer.def_download_manager_recommended_max_bytes_over_mobile);
- if (recommendedMaxBytes > 0) {
- loadSetting(stmt, Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
- Integer.toString(recommendedMaxBytes));
- }
-
loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
R.integer.def_long_press_timeout_millis);
@@ -1737,15 +1814,6 @@
R.bool.def_lockscreen_disabled);
}
- loadBooleanSetting(stmt, Settings.Secure.DEVICE_PROVISIONED,
- R.bool.def_device_provisioned);
-
- loadBooleanSetting(stmt, Settings.Secure.NETSTATS_ENABLED,
- R.bool.def_netstats_enabled);
-
- loadIntegerSetting(stmt, Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
- R.integer.def_max_dhcp_retries);
-
loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ENABLED,
R.bool.def_screensaver_enabled);
loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
@@ -1756,6 +1824,16 @@
R.string.def_screensaver_component);
loadStringSetting(stmt, Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
R.string.def_screensaver_component);
+
+ loadBooleanSetting(stmt, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ R.bool.def_accessibility_display_magnification_enabled);
+
+ loadFractionSetting(stmt, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ R.fraction.def_accessibility_display_magnification_scale, 1);
+
+ loadBooleanSetting(stmt,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ R.bool.def_accessibility_display_magnification_auto_update);
} finally {
if (stmt != null) stmt.close();
}
@@ -1769,6 +1847,97 @@
R.string.def_backup_transport);
}
+ private void loadGlobalSettings(SQLiteDatabase db) {
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+ + " VALUES(?,?);");
+
+ // --- Previously in 'system'
+ loadBooleanSetting(stmt, Settings.Global.AIRPLANE_MODE_ON,
+ R.bool.def_airplane_mode_on);
+
+ loadStringSetting(stmt, Settings.Global.AIRPLANE_MODE_RADIOS,
+ R.string.def_airplane_mode_radios);
+
+ loadStringSetting(stmt, Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+ R.string.airplane_mode_toggleable_radios);
+
+ loadBooleanSetting(stmt, Settings.Global.ASSISTED_GPS_ENABLED,
+ R.bool.assisted_gps_enabled);
+
+ loadBooleanSetting(stmt, Settings.Global.AUTO_TIME,
+ R.bool.def_auto_time); // Sync time to NITZ
+
+ loadBooleanSetting(stmt, Settings.Global.AUTO_TIME_ZONE,
+ R.bool.def_auto_time_zone); // Sync timezone to NITZ
+
+ loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
+ ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
+ mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
+ ? 1 : 0);
+
+ loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
+ R.integer.def_wifi_sleep_policy);
+
+ // --- Previously in 'secure'
+ loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
+ R.bool.def_bluetooth_on);
+
+ // Enable or disable Cell Broadcast SMS
+ loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
+ RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
+
+ // Data roaming default, based on build
+ loadSetting(stmt, Settings.Global.DATA_ROAMING,
+ "true".equalsIgnoreCase(
+ SystemProperties.get("ro.com.android.dataroaming",
+ "false")) ? 1 : 0);
+
+ loadBooleanSetting(stmt, Settings.Global.DEVICE_PROVISIONED,
+ R.bool.def_device_provisioned);
+
+ final int maxBytes = mContext.getResources().getInteger(
+ R.integer.def_download_manager_max_bytes_over_mobile);
+ if (maxBytes > 0) {
+ loadSetting(stmt, Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
+ Integer.toString(maxBytes));
+ }
+
+ final int recommendedMaxBytes = mContext.getResources().getInteger(
+ R.integer.def_download_manager_recommended_max_bytes_over_mobile);
+ if (recommendedMaxBytes > 0) {
+ loadSetting(stmt, Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
+ Integer.toString(recommendedMaxBytes));
+ }
+
+ // Mobile Data default, based on build
+ loadSetting(stmt, Settings.Global.MOBILE_DATA,
+ "true".equalsIgnoreCase(
+ SystemProperties.get("ro.com.android.mobiledata",
+ "true")) ? 1 : 0);
+
+ loadBooleanSetting(stmt, Settings.Global.NETSTATS_ENABLED,
+ R.bool.def_netstats_enabled);
+
+ loadBooleanSetting(stmt, Settings.Global.INSTALL_NON_MARKET_APPS,
+ R.bool.def_install_non_market_apps);
+
+ loadIntegerSetting(stmt, Settings.Global.NETWORK_PREFERENCE,
+ R.integer.def_network_preference);
+
+ loadBooleanSetting(stmt, Settings.Global.USB_MASS_STORAGE_ENABLED,
+ R.bool.def_usb_mass_storage_enabled);
+
+ loadIntegerSetting(stmt, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
+ R.integer.def_max_dhcp_retries);
+
+ // --- New global settings start here
+ } finally {
+ if (stmt != null) stmt.close();
+ }
+ }
+
private void loadSetting(SQLiteStatement stmt, String key, Object value) {
stmt.bindString(1, key);
stmt.bindString(2, value.toString());
@@ -1795,7 +1964,7 @@
}
private int getIntValueFromSystem(SQLiteDatabase db, String name, int defaultValue) {
- return getIntValueFromTable(db, "system", name, defaultValue);
+ return getIntValueFromTable(db, TABLE_SYSTEM, name, defaultValue);
}
private int getIntValueFromTable(SQLiteDatabase db, String table, String name,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1fa3695..b444eb1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,53 +18,73 @@
import java.io.FileNotFoundException;
import java.security.SecureRandom;
+import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import android.app.ActivityManagerNative;
import android.app.backup.BackupManager;
+import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
+import android.util.Slog;
+import android.util.SparseArray;
public class SettingsProvider extends ContentProvider {
private static final String TAG = "SettingsProvider";
private static final boolean LOCAL_LOGV = false;
+ private static final String TABLE_SYSTEM = "system";
+ private static final String TABLE_SECURE = "secure";
+ private static final String TABLE_GLOBAL = "global";
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_OLD_FAVORITES = "old_favorites";
private static final String[] COLUMN_VALUE = new String[] { "value" };
- // Cache for settings, access-ordered for acting as LRU.
+ // Caches for each user's settings, access-ordered for acting as LRU.
// Guarded by themselves.
private static final int MAX_CACHE_ENTRIES = 200;
- private static final SettingsCache sSystemCache = new SettingsCache("system");
- private static final SettingsCache sSecureCache = new SettingsCache("secure");
+ private static final SparseArray<SettingsCache> sSystemCaches
+ = new SparseArray<SettingsCache>();
+ private static final SparseArray<SettingsCache> sSecureCaches
+ = new SparseArray<SettingsCache>();
+ private static final SettingsCache sGlobalCache = new SettingsCache(TABLE_GLOBAL);
// The count of how many known (handled by SettingsProvider)
- // database mutations are currently being handled. Used by
- // sFileObserver to not reload the database when it's ourselves
+ // database mutations are currently being handled for this user.
+ // Used by file observers to not reload the database when it's ourselves
// modifying it.
- private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0);
+ private static final SparseArray<AtomicInteger> sKnownMutationsInFlight
+ = new SparseArray<AtomicInteger>();
// Over this size we don't reject loading or saving settings but
// we do consider them broken/malicious and don't keep them in
@@ -77,10 +97,131 @@
// want to cache the existence of a key, but not store its value.
private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
- protected DatabaseHelper mOpenHelper;
+ // Each defined user has their own settings
+ protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
+ //protected DatabaseHelper mOpenHelper;
+ private UserManager mUserManager;
private BackupManager mBackupManager;
/**
+ * Settings which need to be treated as global/shared in multi-user environments.
+ */
+ static final HashSet<String> sSecureGlobalKeys;
+ static final HashSet<String> sSystemGlobalKeys;
+ static {
+ // Keys (name column) from the 'secure' table that are now in the owner user's 'global'
+ // table, shared across all users
+ // These must match Settings.Secure.MOVED_TO_GLOBAL
+ sSecureGlobalKeys = new HashSet<String>();
+ sSecureGlobalKeys.add(Settings.Secure.ASSISTED_GPS_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.CDMA_CELL_BROADCAST_SMS);
+ sSecureGlobalKeys.add(Settings.Secure.CDMA_ROAMING_MODE);
+ sSecureGlobalKeys.add(Settings.Secure.CDMA_SUBSCRIPTION_MODE);
+ sSecureGlobalKeys.add(Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE);
+ sSecureGlobalKeys.add(Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI);
+ sSecureGlobalKeys.add(Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.DISPLAY_DENSITY_FORCED);
+ sSecureGlobalKeys.add(Settings.Secure.DISPLAY_SIZE_FORCED);
+ sSecureGlobalKeys.add(Settings.Secure.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
+ sSecureGlobalKeys.add(Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+ sSecureGlobalKeys.add(Settings.Secure.MOBILE_DATA);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_BUCKET_DURATION);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_DELETE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_PERSIST_BYTES);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_ROTATE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_POLL_INTERVAL);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_REPORT_XT_OVER_DEV);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_SAMPLE_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_BUCKET_DURATION);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_DELETE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_PERSIST_BYTES);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_ROTATE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_BUCKET_DURATION);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_DELETE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_PERSIST_BYTES);
+ sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_ROTATE_AGE);
+ sSecureGlobalKeys.add(Settings.Secure.NETWORK_PREFERENCE);
+ sSecureGlobalKeys.add(Settings.Secure.NITZ_UPDATE_DIFF);
+ sSecureGlobalKeys.add(Settings.Secure.NITZ_UPDATE_SPACING);
+ sSecureGlobalKeys.add(Settings.Secure.NTP_SERVER);
+ sSecureGlobalKeys.add(Settings.Secure.NTP_TIMEOUT);
+ sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT);
+ sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS);
+ sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT);
+ sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS);
+ sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT);
+ sSecureGlobalKeys.add(Settings.Secure.SAMPLING_PROFILER_MS);
+ sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DATA_SERVICE_URL);
+ sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DETECTION_REDIR_HOST);
+ sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DETECTION_TARGET_URL);
+ sSecureGlobalKeys.add(Settings.Secure.TETHER_DUN_APN);
+ sSecureGlobalKeys.add(Settings.Secure.TETHER_DUN_REQUIRED);
+ sSecureGlobalKeys.add(Settings.Secure.TETHER_SUPPORTED);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_HELP_URI);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_NOTIFICATION_TYPE);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_POLLING_SEC);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_RESET_DAY);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_THRESHOLD_BYTES);
+ sSecureGlobalKeys.add(Settings.Secure.THROTTLE_VALUE_KBITSPS);
+ sSecureGlobalKeys.add(Settings.Secure.USE_GOOGLE_MAIL);
+ sSecureGlobalKeys.add(Settings.Secure.WEB_AUTOFILL_QUERY_URL);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_COUNTRY_CODE);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_FREQUENCY_BAND);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_IDLE_MS);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_ON);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_P2P_DEVICE_NAME);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_SAVED_STATE);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_NUM_ARP_PINGS);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_ON);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
+ sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
+ sSecureGlobalKeys.add(Settings.Secure.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+ sSecureGlobalKeys.add(Settings.Secure.WTF_IS_FATAL);
+
+ // Keys from the 'system' table now moved to 'global'
+ // These must match Settings.System.MOVED_TO_GLOBAL
+ sSystemGlobalKeys = new HashSet<String>();
+ sSystemGlobalKeys.add(Settings.Secure.ADB_ENABLED);
+ sSystemGlobalKeys.add(Settings.Secure.BLUETOOTH_ON);
+ sSystemGlobalKeys.add(Settings.Secure.DATA_ROAMING);
+ sSystemGlobalKeys.add(Settings.Secure.DEVICE_PROVISIONED);
+ sSystemGlobalKeys.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
+ sSystemGlobalKeys.add(Settings.Secure.USB_MASS_STORAGE_ENABLED);
+
+ sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_ON);
+ sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_RADIOS);
+ sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ sSystemGlobalKeys.add(Settings.System.AUTO_TIME);
+ sSystemGlobalKeys.add(Settings.System.AUTO_TIME_ZONE);
+ sSystemGlobalKeys.add(Settings.System.CAR_DOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.CAR_UNDOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.DESK_DOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.DESK_UNDOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.DOCK_SOUNDS_ENABLED);
+ sSystemGlobalKeys.add(Settings.System.LOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.UNLOCK_SOUND);
+ sSystemGlobalKeys.add(Settings.System.LOW_BATTERY_SOUND);
+ sSystemGlobalKeys.add(Settings.System.POWER_SOUNDS_ENABLED);
+ sSystemGlobalKeys.add(Settings.System.WIFI_SLEEP_POLICY);
+ }
+
+ private boolean settingMovedToGlobal(final String name) {
+ return sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name);
+ }
+
+ /**
* Decode a content URL into the table, projection, and arguments
* used to access the corresponding database rows.
*/
@@ -107,7 +248,7 @@
if (!DatabaseHelper.isValidTable(this.table)) {
throw new IllegalArgumentException("Bad root path: " + this.table);
}
- if ("system".equals(this.table) || "secure".equals(this.table)) {
+ if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table)) {
this.where = Settings.NameValueTable.NAME + "=?";
this.args = new String[] { url.getPathSegments().get(1) };
} else {
@@ -144,7 +285,9 @@
throw new IllegalArgumentException("Invalid URI: " + tableUri);
}
String table = tableUri.getPathSegments().get(0);
- if ("system".equals(table) || "secure".equals(table)) {
+ if (TABLE_SYSTEM.equals(table) ||
+ TABLE_SECURE.equals(table) ||
+ TABLE_GLOBAL.equals(table)) {
String name = values.getAsString(Settings.NameValueTable.NAME);
return Uri.withAppendedPath(tableUri, name);
} else {
@@ -159,18 +302,21 @@
* contract class uses these to provide client-side caches.)
* @param uri to send notifications for
*/
- private void sendNotify(Uri uri) {
+ private void sendNotify(Uri uri, int userHandle) {
// Update the system property *first*, so if someone is listening for
// a notification and then using the contract class to get their data,
// the system property will be updated and they'll get the new data.
boolean backedUpDataChanged = false;
String property = null, table = uri.getPathSegments().get(0);
- if (table.equals("system")) {
- property = Settings.System.SYS_PROP_SETTING_VERSION;
+ if (table.equals(TABLE_SYSTEM)) {
+ property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle;
backedUpDataChanged = true;
- } else if (table.equals("secure")) {
- property = Settings.Secure.SYS_PROP_SETTING_VERSION;
+ } else if (table.equals(TABLE_SECURE)) {
+ property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle;
+ backedUpDataChanged = true;
+ } else if (table.equals(TABLE_GLOBAL)) {
+ property = Settings.Global.SYS_PROP_SETTING_VERSION; // this one is global
backedUpDataChanged = true;
}
@@ -201,7 +347,7 @@
* @throws SecurityException if the caller is forbidden to write.
*/
private void checkWritePermissions(SqlArguments args) {
- if ("secure".equals(args.table) &&
+ if ((TABLE_SECURE.equals(args.table) || TABLE_GLOBAL.equals(args.table)) &&
getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
PackageManager.PERMISSION_GRANTED) {
@@ -218,70 +364,147 @@
// normally the exclusive owner of the database. But we keep this
// enabled all the time to minimize development-vs-user
// differences in testing.
- private static SettingsFileObserver sObserverInstance;
+ private static SparseArray<SettingsFileObserver> sObserverInstances
+ = new SparseArray<SettingsFileObserver>();
private class SettingsFileObserver extends FileObserver {
private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
+ private final int mUserHandle;
private final String mPath;
- public SettingsFileObserver(String path) {
+ public SettingsFileObserver(int userHandle, String path) {
super(path, FileObserver.CLOSE_WRITE |
FileObserver.CREATE | FileObserver.DELETE |
FileObserver.MOVED_TO | FileObserver.MODIFY);
+ mUserHandle = userHandle;
mPath = path;
}
public void onEvent(int event, String path) {
- int modsInFlight = sKnownMutationsInFlight.get();
+ int modsInFlight = sKnownMutationsInFlight.get(mUserHandle).get();
if (modsInFlight > 0) {
// our own modification.
return;
}
- Log.d(TAG, "external modification to " + mPath + "; event=" + event);
+ Log.d(TAG, "User " + mUserHandle + " external modification to " + mPath
+ + "; event=" + event);
if (!mIsDirty.compareAndSet(false, true)) {
// already handled. (we get a few update events
// during an sqlite write)
return;
}
- Log.d(TAG, "updating our caches for " + mPath);
- fullyPopulateCaches();
+ Log.d(TAG, "User " + mUserHandle + " updating our caches for " + mPath);
+ fullyPopulateCaches(mUserHandle);
mIsDirty.set(false);
}
}
@Override
public boolean onCreate() {
- mOpenHelper = new DatabaseHelper(getContext());
mBackupManager = new BackupManager(getContext());
+ mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
- if (!ensureAndroidIdIsSet()) {
- return false;
+ synchronized (this) {
+ establishDbTrackingLocked(UserHandle.USER_OWNER);
+
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ getContext().registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_OWNER);
+ if (userHandle != UserHandle.USER_OWNER) {
+ onUserRemoved(userHandle);
+ }
+ }
+ }
+ }, userFilter);
+
+ if (!ensureAndroidIdIsSet()) {
+ return false;
+ }
}
-
- // Watch for external modifications to the database file,
- // keeping our cache in sync.
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- sObserverInstance = new SettingsFileObserver(db.getPath());
- sObserverInstance.startWatching();
- startAsyncCachePopulation();
return true;
}
- private void startAsyncCachePopulation() {
- new Thread("populate-settings-caches") {
- public void run() {
- fullyPopulateCaches();
+ void onUserRemoved(int userHandle) {
+ // the db file itself will be deleted automatically, but we need to tear down
+ // our caches and other internal bookkeeping. Creation/deletion of a user's
+ // settings db infrastructure is synchronized on 'this'
+ synchronized (this) {
+ FileObserver observer = sObserverInstances.get(userHandle);
+ if (observer != null) {
+ observer.stopWatching();
+ sObserverInstances.delete(userHandle);
}
- }.start();
+
+ mOpenHelpers.delete(userHandle);
+ sSystemCaches.delete(userHandle);
+ sSecureCaches.delete(userHandle);
+ sKnownMutationsInFlight.delete(userHandle);
+
+ String property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle;
+ SystemProperties.set(property, "");
+ property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle;
+ SystemProperties.set(property, "");
+ }
}
- private void fullyPopulateCaches() {
- fullyPopulateCache("secure", sSecureCache);
- fullyPopulateCache("system", sSystemCache);
+ private void establishDbTrackingLocked(int userHandle) {
+ if (LOCAL_LOGV) {
+ Slog.i(TAG, "Installing settings db helper and caches for user " + userHandle);
+ }
+
+ DatabaseHelper dbhelper = new DatabaseHelper(getContext(), userHandle);
+ mOpenHelpers.append(userHandle, dbhelper);
+
+ // Watch for external modifications to the database files,
+ // keeping our caches in sync.
+ sSystemCaches.append(userHandle, new SettingsCache(TABLE_SYSTEM));
+ sSecureCaches.append(userHandle, new SettingsCache(TABLE_SECURE));
+ sKnownMutationsInFlight.append(userHandle, new AtomicInteger(0));
+ SQLiteDatabase db = dbhelper.getWritableDatabase();
+
+ // Now we can start observing it for changes
+ SettingsFileObserver observer = new SettingsFileObserver(userHandle, db.getPath());
+ sObserverInstances.append(userHandle, observer);
+ observer.startWatching();
+
+ startAsyncCachePopulation(userHandle);
+ }
+
+ class CachePrefetchThread extends Thread {
+ private int mUserHandle;
+
+ CachePrefetchThread(int userHandle) {
+ super("populate-settings-caches");
+ mUserHandle = userHandle;
+ }
+
+ @Override
+ public void run() {
+ fullyPopulateCaches(mUserHandle);
+ }
+ }
+
+ private void startAsyncCachePopulation(int userHandle) {
+ new CachePrefetchThread(userHandle).start();
+ }
+
+ private void fullyPopulateCaches(final int userHandle) {
+ DatabaseHelper dbHelper = mOpenHelpers.get(userHandle);
+ // Only populate the globals cache once, for the owning user
+ if (userHandle == UserHandle.USER_OWNER) {
+ fullyPopulateCache(dbHelper, TABLE_GLOBAL, sGlobalCache);
+ }
+ fullyPopulateCache(dbHelper, TABLE_SECURE, sSecureCaches.get(userHandle));
+ fullyPopulateCache(dbHelper, TABLE_SYSTEM, sSystemCaches.get(userHandle));
}
// Slurp all values (if sane in number & size) into cache.
- private void fullyPopulateCache(String table, SettingsCache cache) {
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ private void fullyPopulateCache(DatabaseHelper dbHelper, String table, SettingsCache cache) {
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = db.query(
table,
new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
@@ -337,23 +560,154 @@
}
}
+ // Lazy-initialize the settings caches for non-primary users
+ private SettingsCache getOrConstructCache(int callingUser, SparseArray<SettingsCache> which) {
+ synchronized (this) {
+ getOrEstablishDatabaseLocked(callingUser); // ignore return value; we don't need it
+ return which.get(callingUser);
+ }
+ }
+
+ // Lazy initialize the database helper and caches for this user, if necessary
+ private DatabaseHelper getOrEstablishDatabaseLocked(int callingUser) {
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ DatabaseHelper dbHelper = mOpenHelpers.get(callingUser);
+ if (null == dbHelper) {
+ establishDbTrackingLocked(callingUser);
+ dbHelper = mOpenHelpers.get(callingUser);
+ }
+ return dbHelper;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
+ public SettingsCache cacheForTable(final int callingUser, String tableName) {
+ if (TABLE_SYSTEM.equals(tableName)) {
+ return getOrConstructCache(callingUser, sSystemCaches);
+ }
+ if (TABLE_SECURE.equals(tableName)) {
+ return getOrConstructCache(callingUser, sSecureCaches);
+ }
+ if (TABLE_GLOBAL.equals(tableName)) {
+ return sGlobalCache;
+ }
+ return null;
+ }
+
+ /**
+ * Used for wiping a whole cache on deletes when we're not
+ * sure what exactly was deleted or changed.
+ */
+ public void invalidateCache(final int callingUser, String tableName) {
+ SettingsCache cache = cacheForTable(callingUser, tableName);
+ if (cache == null) {
+ return;
+ }
+ synchronized (cache) {
+ cache.evictAll();
+ cache.mCacheFullyMatchesDisk = false;
+ }
+ }
+
/**
* Fast path that avoids the use of chatty remoted Cursors.
*/
@Override
public Bundle call(String method, String request, Bundle args) {
+ int callingUser = UserHandle.getCallingUserId();
+ if (args != null) {
+ int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser);
+ if (reqUser != callingUser) {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Not permitted to access settings for other users");
+ if (reqUser == UserHandle.USER_CURRENT) {
+ try {
+ reqUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ // can't happen
+ }
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, " USER_CURRENT resolved to " + reqUser);
+ }
+ }
+ if (reqUser < 0) {
+ throw new IllegalArgumentException("Bad user handle " + reqUser);
+ }
+ callingUser = reqUser;
+ if (LOCAL_LOGV) Slog.v(TAG, " fetching setting for user " + callingUser);
+ }
+ }
+
+ // Note: we assume that get/put operations for moved-to-global names have already
+ // been directed to the new location on the caller side (otherwise we'd fix them
+ // up here).
+
+ DatabaseHelper dbHelper;
+ SettingsCache cache;
+
+ // Get methods
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
- return lookupValue("system", sSystemCache, request);
+ if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
+ synchronized (this) {
+ dbHelper = getOrEstablishDatabaseLocked(callingUser);
+ cache = sSystemCaches.get(callingUser);
+ }
+ return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
}
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
- return lookupValue("secure", sSecureCache, request);
+ if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
+ synchronized (this) {
+ dbHelper = getOrEstablishDatabaseLocked(callingUser);
+ cache = sSecureCaches.get(callingUser);
+ }
+ return lookupValue(dbHelper, TABLE_SECURE, cache, request);
}
+ if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
+ if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
+ // fast path: owner db & cache are immutable after onCreate() so we need not
+ // guard on the attempt to look them up
+ return lookupValue(getOrEstablishDatabaseLocked(UserHandle.USER_OWNER), TABLE_GLOBAL,
+ sGlobalCache, request);
+ }
+
+ // Put methods - new value is in the args bundle under the key named by
+ // the Settings.NameValueTable.VALUE static.
+ final String newValue = (args == null)
+ ? null : args.getString(Settings.NameValueTable.VALUE);
+ if (newValue == null) {
+ throw new IllegalArgumentException("Bad value for " + method);
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(Settings.NameValueTable.NAME, request);
+ values.put(Settings.NameValueTable.VALUE, newValue);
+ if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
+ if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
+ insert(Settings.System.CONTENT_URI, values);
+ } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
+ if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
+ insert(Settings.Secure.CONTENT_URI, values);
+ } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
+ if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
+ insert(Settings.Global.CONTENT_URI, values);
+ } else {
+ Slog.w(TAG, "call() with invalid method: " + method);
+ }
+
return null;
}
// Looks up value 'key' in 'table' and returns either a single-pair Bundle,
// possibly with a null value, or null on failure.
- private Bundle lookupValue(String table, SettingsCache cache, String key) {
+ private Bundle lookupValue(DatabaseHelper dbHelper, String table,
+ final SettingsCache cache, String key) {
+ if (cache == null) {
+ Slog.e(TAG, "cache is null for user " + UserHandle.getCallingUserId() + " : key=" + key);
+ return null;
+ }
synchronized (cache) {
Bundle value = cache.get(key);
if (value != null) {
@@ -372,7 +726,7 @@
}
}
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
@@ -393,8 +747,14 @@
@Override
public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
+ final int callingUser = UserHandle.getCallingUserId();
+ if (LOCAL_LOGV) Slog.v(TAG, "query() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ DatabaseHelper dbH;
+ synchronized (this) {
+ dbH = getOrEstablishDatabaseLocked(callingUser);
+ }
+ SQLiteDatabase db = dbH.getReadableDatabase();
// The favorites table was moved from this provider to a provider inside Home
// Home still need to query this table to upgrade from pre-cupcake builds
@@ -437,15 +797,22 @@
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
+ final int callingUser = UserHandle.getCallingUserId();
+ if (LOCAL_LOGV) Slog.v(TAG, "bulkInsert() for user " + callingUser);
SqlArguments args = new SqlArguments(uri);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
}
checkWritePermissions(args);
- SettingsCache cache = SettingsCache.forTable(args.table);
+ SettingsCache cache = cacheForTable(callingUser, args.table);
- sKnownMutationsInFlight.incrementAndGet();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
+ mutationCount.incrementAndGet();
+ DatabaseHelper dbH;
+ synchronized (this) {
+ dbH = getOrEstablishDatabaseLocked(callingUser);
+ }
+ SQLiteDatabase db = dbH.getWritableDatabase();
db.beginTransaction();
try {
int numValues = values.length;
@@ -457,10 +824,10 @@
db.setTransactionSuccessful();
} finally {
db.endTransaction();
- sKnownMutationsInFlight.decrementAndGet();
+ mutationCount.decrementAndGet();
}
- sendNotify(uri);
+ sendNotify(uri, callingUser);
return values.length;
}
@@ -538,6 +905,22 @@
@Override
public Uri insert(Uri url, ContentValues initialValues) {
+ return insertForUser(url, initialValues, UserHandle.getCallingUserId());
+ }
+
+ // Settings.put*ForUser() always winds up here, so this is where we apply
+ // policy around permission to write settings for other users.
+ private Uri insertForUser(Uri url, ContentValues initialValues, int desiredUserHandle) {
+ final int callingUser = UserHandle.getCallingUserId();
+ if (callingUser != desiredUserHandle) {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Not permitted to access settings for other users");
+ }
+
+ if (LOCAL_LOGV) Slog.v(TAG, "insert(" + url + ") for user " + desiredUserHandle
+ + " by " + callingUser);
+
SqlArguments args = new SqlArguments(url);
if (TABLE_FAVORITES.equals(args.table)) {
return null;
@@ -551,28 +934,41 @@
if (!parseProviderList(url, initialValues)) return null;
}
- SettingsCache cache = SettingsCache.forTable(args.table);
+ // The global table is stored under the owner, always
+ if (TABLE_GLOBAL.equals(args.table)) {
+ desiredUserHandle = UserHandle.USER_OWNER;
+ }
+
+ SettingsCache cache = cacheForTable(desiredUserHandle, args.table);
String value = initialValues.getAsString(Settings.NameValueTable.VALUE);
if (SettingsCache.isRedundantSetValue(cache, name, value)) {
return Uri.withAppendedPath(url, name);
}
- sKnownMutationsInFlight.incrementAndGet();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final AtomicInteger mutationCount = sKnownMutationsInFlight.get(desiredUserHandle);
+ mutationCount.incrementAndGet();
+ DatabaseHelper dbH;
+ synchronized (this) {
+ dbH = getOrEstablishDatabaseLocked(callingUser);
+ }
+ SQLiteDatabase db = dbH.getWritableDatabase();
final long rowId = db.insert(args.table, null, initialValues);
- sKnownMutationsInFlight.decrementAndGet();
+ mutationCount.decrementAndGet();
if (rowId <= 0) return null;
SettingsCache.populate(cache, initialValues); // before we notify
if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + initialValues);
+ // Note that we use the original url here, not the potentially-rewritten table name
url = getUriFor(url, initialValues, rowId);
- sendNotify(url);
+ sendNotify(url, desiredUserHandle);
return url;
}
@Override
public int delete(Uri url, String where, String[] whereArgs) {
+ final int callingUser = UserHandle.getCallingUserId();
+ if (LOCAL_LOGV) Slog.v(TAG, "delete() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
@@ -581,36 +977,53 @@
}
checkWritePermissions(args);
- sKnownMutationsInFlight.incrementAndGet();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int count = db.delete(args.table, args.where, args.args);
- sKnownMutationsInFlight.decrementAndGet();
- if (count > 0) {
- SettingsCache.invalidate(args.table); // before we notify
- sendNotify(url);
+ final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
+ mutationCount.incrementAndGet();
+ DatabaseHelper dbH;
+ synchronized (this) {
+ dbH = getOrEstablishDatabaseLocked(callingUser);
}
- startAsyncCachePopulation();
+ SQLiteDatabase db = dbH.getWritableDatabase();
+ int count = db.delete(args.table, args.where, args.args);
+ mutationCount.decrementAndGet();
+ if (count > 0) {
+ invalidateCache(callingUser, args.table); // before we notify
+ sendNotify(url, callingUser);
+ }
+ startAsyncCachePopulation(callingUser);
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
return count;
}
@Override
public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
+ // NOTE: update() is never called by the front-end Settings API, and updates that
+ // wind up affecting rows in Secure that are globally shared will not have the
+ // intended effect (the update will be invisible to the rest of the system).
+ // This should have no practical effect, since writes to the Secure db can only
+ // be done by system code, and that code should be using the correct API up front.
+ final int callingUser = UserHandle.getCallingUserId();
+ if (LOCAL_LOGV) Slog.v(TAG, "update() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
}
checkWritePermissions(args);
- sKnownMutationsInFlight.incrementAndGet();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int count = db.update(args.table, initialValues, args.where, args.args);
- sKnownMutationsInFlight.decrementAndGet();
- if (count > 0) {
- SettingsCache.invalidate(args.table); // before we notify
- sendNotify(url);
+ final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
+ mutationCount.incrementAndGet();
+ DatabaseHelper dbH;
+ synchronized (this) {
+ dbH = getOrEstablishDatabaseLocked(callingUser);
}
- startAsyncCachePopulation();
+ SQLiteDatabase db = dbH.getWritableDatabase();
+ int count = db.update(args.table, initialValues, args.where, args.args);
+ mutationCount.decrementAndGet();
+ if (count > 0) {
+ invalidateCache(callingUser, args.table); // before we notify
+ sendNotify(url, callingUser);
+ }
+ startAsyncCachePopulation(callingUser);
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
return count;
}
@@ -772,16 +1185,6 @@
return bundle;
}
- public static SettingsCache forTable(String tableName) {
- if ("system".equals(tableName)) {
- return SettingsProvider.sSystemCache;
- }
- if ("secure".equals(tableName)) {
- return SettingsProvider.sSecureCache;
- }
- return null;
- }
-
/**
* Populates a key in a given (possibly-null) cache.
*/
@@ -809,21 +1212,6 @@
}
/**
- * Used for wiping a whole cache on deletes when we're not
- * sure what exactly was deleted or changed.
- */
- public static void invalidate(String tableName) {
- SettingsCache cache = SettingsCache.forTable(tableName);
- if (cache == null) {
- return;
- }
- synchronized (cache) {
- cache.evictAll();
- cache.mCacheFullyMatchesDisk = false;
- }
- }
-
- /**
* For suppressing duplicate/redundant settings inserts early,
* checking our cache first (but without faulting it in),
* before going to sqlite with the mutation.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b185471..13800a6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -167,7 +167,7 @@
android:name=".BeanBag"
android:exported="true"
android:label="BeanBag"
- android:icon="@drawable/redbeandroid"
+ android:icon="@drawable/redbean2"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true"
android:launchMode="singleInstance"
@@ -184,12 +184,27 @@
<service
android:name=".BeanBagDream"
android:exported="true"
- android:label="Beans in space">
+ android:label="@string/jelly_bean_dream_name"
+ android:enabled="false"
+ >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DREAM" />
</intent-filter>
</service>
+
+ <activity android:name=".Somnambulator"
+ android:label="@string/start_dreams"
+ android:icon="@mipmap/ic_dreams"
+ android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index 6ae32f1..84e6bc8 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
index fdc56bb..782d214 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
index ea7c4e3..a00bc5b 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
index 49d5101..8605701 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
index 33e56e8..38bd0cd 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
index 2fb191d..0c12c16 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
index ce008f3..477df5f 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
index b971088..bd60cd6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
index d2d7842..5272c91 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml b/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
index d683af9..c209055 100644
--- a/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_selected="true" />
<item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_pressed="true" />
- <item android:drawable="@*android:color/transparent"/>
+ <item android:drawable="@drawable/recents_thumbnail_no_press"/>
</selector>
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml b/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml
new file mode 100644
index 0000000..be07b2c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#00000000" />
diff --git a/packages/SystemUI/res/layout-sw600dp/carrier_label.xml b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
new file mode 100644
index 0000000..b33caf8
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
@@ -0,0 +1,23 @@
+<?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.
+*/
+-->
+
+<Space
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:visibility="gone"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 67e13eb..fbbd7e5 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -143,8 +143,13 @@
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
- android:layout_height="@dimen/navigation_bar_deadzone_size"
+ android:layout_height="match_parent"
android:layout_width="match_parent"
+ systemui:minSize="@dimen/navigation_bar_deadzone_size"
+ systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
+ systemui:holdTime="@integer/navigation_bar_deadzone_hold"
+ systemui:decayTime="@integer/navigation_bar_deadzone_decay"
+ systemui:orientation="horizontal"
android:layout_gravity="top"
/>
</FrameLayout>
@@ -269,8 +274,13 @@
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
- android:layout_height="@dimen/navigation_bar_deadzone_size"
+ android:layout_height="match_parent"
android:layout_width="match_parent"
+ systemui:minSize="@dimen/navigation_bar_deadzone_size"
+ systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
+ systemui:holdTime="@integer/navigation_bar_deadzone_hold"
+ systemui:decayTime="@integer/navigation_bar_deadzone_decay"
+ systemui:orientation="vertical"
android:layout_gravity="top"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/carrier_label.xml b/packages/SystemUI/res/layout/carrier_label.xml
new file mode 100644
index 0000000..41a1fff
--- /dev/null
+++ b/packages/SystemUI/res/layout/carrier_label.xml
@@ -0,0 +1,30 @@
+<?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.
+*/
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/carrier_label"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ android:layout_height="@dimen/carrier_label_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="@dimen/close_handle_height"
+ android:gravity="center"
+ android:visibility="invisible"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index d41040d..33b5dbb 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -147,8 +147,13 @@
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
- android:layout_height="@dimen/navigation_bar_deadzone_size"
+ android:layout_height="match_parent"
android:layout_width="match_parent"
+ systemui:minSize="@dimen/navigation_bar_deadzone_size"
+ systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
+ systemui:holdTime="@integer/navigation_bar_deadzone_hold"
+ systemui:decayTime="@integer/navigation_bar_deadzone_decay"
+ systemui:orientation="horizontal"
android:layout_gravity="top"
/>
</FrameLayout>
@@ -276,9 +281,14 @@
<com.android.systemui.statusbar.policy.DeadZone
android:id="@+id/deadzone"
- android:layout_width="@dimen/navigation_bar_deadzone_size"
android:layout_height="match_parent"
- android:layout_gravity="left"
+ android:layout_width="match_parent"
+ systemui:minSize="@dimen/navigation_bar_deadzone_size"
+ systemui:maxSize="@dimen/navigation_bar_deadzone_size_max"
+ systemui:holdTime="@integer/navigation_bar_deadzone_hold"
+ systemui:decayTime="@integer/navigation_bar_deadzone_decay"
+ systemui:orientation="vertical"
+ android:layout_gravity="top"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
index c4b881e..8c6258a 100644
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -19,6 +19,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/settings_panel"
+ android:background="#80000080"
>
<ImageView
android:layout_width="match_parent"
@@ -26,7 +27,6 @@
android:scaleType="centerInside"
android:src="@drawable/qs_coming_soon"
android:padding="4dp"
- android:background="#80000080"
/>
<LinearLayout android:id="@+id/handle"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 8eff1f4..f2e83d8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -29,15 +29,10 @@
android:layout_marginLeft="@dimen/notification_panel_margin_left"
>
- <TextView
- android:id="@+id/carrier_label"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ <include
+ layout="@layout/carrier_label"
android:layout_height="@dimen/carrier_label_height"
android:layout_width="match_parent"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/close_handle_height"
- android:gravity="center"
- android:visibility="invisible"
/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ad6b8f4..5e0d1e8 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -40,11 +40,11 @@
>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
<include layout="@layout/quick_settings"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PanelHolder>
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
new file mode 100644
index 0000000..56cbac1
--- /dev/null
+++ b/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
new file mode 100644
index 0000000..ea3d991
--- /dev/null
+++ b/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
new file mode 100644
index 0000000..ddc7f66
--- /dev/null
+++ b/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d5e4c0a..38ea24d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Die skerm sal outomaties draai."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skerm is in landskapsoriëntasie gesluit."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skerm is in portretoriëntasie gesluit."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f34ed8e..9c0b934 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -60,7 +60,7 @@
<string name="always_use_accessory" msgid="1210954576979621596">"ለእዚህ USB ተቀጥላ በነባሪነት ተጠቀም"</string>
<string name="usb_debugging_title" msgid="1114766024068112429">"የUSB ማረሚያ ይፈቀድ?"</string>
<string name="usb_debugging_message" msgid="719863946976291180">"የUSB ማረም ከዚህ ኮምፒውተር ይፈቀድ?"\n"የእርስዎ RSA ቁልፍ ጣት አሻራ "\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g> ነው"</string>
- <string name="usb_debugging_always" msgid="4253099426793114693">"ለእዚህ ኮምፒውተር ሁልጊዜ ፍቀድ"</string>
+ <string name="usb_debugging_always" msgid="4253099426793114693">"ለዚህ ኮምፒውተር ሁልጊዜ ፍቀድ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ማያ እንዲሞላ አጉላ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ማያ ለመሙለት ሳብ"</string>
<string name="compat_mode_help_header" msgid="7969493989397529910">"የተኳኋኝነት አጉላ"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ማያ ገጽ በራስ ሰር ይዞራል።"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ማያ ገጽ በወርድ ገፅ አቀማመጥ ተቆልፏል።"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ማያ ገጽ በቁም ገፅ አቀማመጥ ተቆልፏል።"</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 6540f0a..59a1083 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"سيتم تدوير الشاشة تلقائيًا."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"تم تأمين الشاشة في الاتجاه الأفقي."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"تم تأمين الشاشة في الاتجاه العمودي."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 8216e2b..394d237 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран паварочваецца аўтаматычна."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 56b8bb3..c24ac7f 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екранът ще се завърта автоматично."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екранът е заключен в хоризонтална ориентация."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екранът е заключен във вертикална ориентация."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3eb078d..3b3c0bc 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girarà automàticament."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla està bloquejada en orientació horitzontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla està bloquejada en orientació vertical."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a7bf519..a471e98 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Obrazovka se automaticky otočí."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamčena v orientaci na šířku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamčena v orientaci na výšku."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 48cab36..6da3992 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skærmen roterer automatisk."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skærmen er nu låst i stående retning."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4bcbc31..7ae9ca6 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Bildschirm wird automatisch gedreht."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildschirm bleibt im Querformat."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildschirm bleibt im Hochformat."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 2231a8c..7cd4fc2 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Θα γίνεται αυτόματη περιστροφή της οθόνης."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Η οθόνη έχει κλειδωθεί σε οριζόντιο προσανατολισμό."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Η οθόνη έχει κλειδωθεί σε κατακόρυφο προσανατολισμό."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 671ac1b..1fb98f0 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Screen will rotate automatically."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Screen is locked in portrait orientation."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e0aa3f9..89c6a9c 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -59,7 +59,7 @@
<string name="always_use_device" msgid="1450287437017315906">"Se usa de forma predeterminada para este dispositivo USB."</string>
<string name="always_use_accessory" msgid="1210954576979621596">"Se usa de forma predeterminada para este accesorio USB."</string>
<string name="usb_debugging_title" msgid="1114766024068112429">"¿Permitir la depuración de USB?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"¿Quieres permitir la depuración de USB desde esta computadora?"\n"La huella digital de tu clave RSA es"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>."</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"¿Quieres permitir la depuración de USB desde esta computadora?"\n"La huella digital de tu clave RSA es:"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"Permitir el uso de esta computadora siempre"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ocupar la pantalla"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Estirar p/ ocupar la pantalla"</string>
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index cd03d92..42441d5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 4348bda..9df87db 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekraani pööramine toimub automaatselt."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekraan on lukustatud horisontaalsuunas."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekraan on lukustatud vertikaalsuunas."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 66d65d9..bf9ac48 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -73,8 +73,8 @@
<string name="screenshot_failed_title" msgid="705781116746922771">"تصویر صفحه گرفته نشد."</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"تصویر صفحه ذخیره نشد. ممکن است دستگاه ذخیره در حال استفاده باشد."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
- <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب به عنوان دستگاه پخش رسانه (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب به عنوان دوربین (PTP)"</string>
+ <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب بهعنوان دوربین (PTP)"</string>
<string name="installer_cd_button_title" msgid="2312667578562201583">"برنامه Android File Transfer را برای Mac نصب کنید"</string>
<string name="accessibility_back" msgid="567011538994429120">"برگشت"</string>
<string name="accessibility_home" msgid="8217216074895377641">"صفحهٔ اصلی"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"صفحه به صورت خودکار میچرخد."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"صفحه اکنون در جهت افقی قفل است."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"صفحه اکنون در جهت عمودی قفل است."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 5132502..ba23073 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ruutu kääntyy automaattisesti."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ruutu on lukittu vaakasuuntaan."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ruutu on lukittu pystysuuntaan."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 18f8c9d..b08b36a 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"L\'écran pivote automatiquement."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"L\'écran est verrouillé en mode portrait."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 16e1443..d87bc05 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -58,8 +58,8 @@
<string name="label_view" msgid="6304565553218192990">"देखें"</string>
<string name="always_use_device" msgid="1450287437017315906">"इस USB उपकरण के लिए डिफ़ॉल्ट रूप से उपयोग करें"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"इस USB एसेसरी के लिए डिफ़ॉल्ट रूप से उपयोग करें"</string>
- <string name="usb_debugging_title" msgid="1114766024068112429">"USB डीबग करने दें?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"इस कंप्यूटर से USB डीबग करने दें?"\n"आपका RSA कुंजी फ़िंगरप्रिंट यह है:"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_title" msgid="1114766024068112429">"USB डीबगिंग करने दें?"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"इस कंप्यूटर से USB डीबगिंग करने दें?"\n"आपका RSA कुंजी फ़िंगरप्रिंट यह है:"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"इस कंप्यूटर को हमेशा अनुमति दें"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरने हेतु ज़ूम करें"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन को भरने के लिए खींचें"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रीन स्वचालित रूप से घूमेगी."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"स्क्रीन लैंडस्केप अभिविन्यास में लॉक है."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"स्क्रीन पोर्ट्रेट अभिविन्यास में लॉक है."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 830b73c..7b37372 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -58,8 +58,8 @@
<string name="label_view" msgid="6304565553218192990">"Prikaži"</string>
<string name="always_use_device" msgid="1450287437017315906">"Koristi se prema zadanim postavkama za ovaj USB uređaj"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"Koristi se prema zadanim postavkama za ovaj USB pribor"</string>
- <string name="usb_debugging_title" msgid="1114766024068112429">"Omogućiti rješavanje programske pogreške na USB-u?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"Omogućiti rješavanje programske pogreške na USB-u na ovom računalu?"\n"Vaš je otisak prsta RSA ključa"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_title" msgid="1114766024068112429">"Omogućiti USB Debugging?"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"Omogućiti USB Debugging na ovom računalu?"\n"Vaš je otisak prsta RSA ključa"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"Uvijek dopusti ovom računalu"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj i ispuni zaslon"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Rastegni i ispuni zaslon"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon će se automatski zakrenuti."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaključan u pejzažnoj orijentaciji."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaključan u portretnoj orijentaciji."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 4d49939..9d9c73a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A képernyő automatikusan forogni fog."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A képernyő zárolva van fekvő tájolásban."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A képernyő zárolva van álló tájolásban."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 65e47dd..0f3e1b7 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Layar akan diputar secara otomatis."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 49b0fb0..93429ed 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Lo schermo ruoterà automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Lo schermo è bloccato in orientamento orizzontale."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Lo schermo è bloccato in orientamento verticale."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 59730af..4495c2d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"המסך יסתובב באופן אוטומטי."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"המסך נעול כעת לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"המסך נעול כעת לאורך."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a9fe4ae..2f0d6da 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -68,7 +68,7 @@
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"スクリーンショットを保存中..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"スクリーンショットを保存しています..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"スクリーンショットを保存しています。"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットをキャプチャしました。"</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットを取得しました。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"タップしてスクリーンショットを表示します。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"スクリーンショットをキャプチャできませんでした。"</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"スクリーンショットを保存できませんでした。ストレージが使用中の可能性があります。"</string>
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"画面は自動的に回転します。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"画面は横向きにロックされています。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"画面は縦向きにロックされています。"</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index bd1cf03..709e14d 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"화면이 자동으로 회전됩니다."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"화면이 가로 방향으로 잠겨 있습니다."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"화면이 세로 방향으로 잠겨 있습니다."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a5a11e2..73de676 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekranas bus sukamas automatiškai."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Užrakintas ekranas yra horizontalios orientacijos."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Užrakintas ekranas yra vertikalios orientacijos."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index c18b03f..ddc2dbb 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekrāns tiks pagriezts automātiski."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index ef926ac..191ee63 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrin akan berputar secara automatik."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrin dikunci dalam orientasi landskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrin dikunci dalam orientasi potret."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index e7005fa..8aa5f26 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skjermen roterer automatisk."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skjermen er låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skjermen er låst i stående retning."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index d5e8444..b740dd4 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Scherm wordt automatisch geroteerd."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Het scherm is nu vergrendeld in liggende stand."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Het scherm is nu vergrendeld in staande stand."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 7639c84..e60c3b9 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran zostanie obrócony automatycznie."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran jest zablokowany w orientacji poziomej."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran jest zablokowany w orientacji pionowej."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 4b41ca0..b2e008c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"O ecrã será rodado automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"O ecrã está bloqueado na orientação horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"O ecrã está bloqueado na orientação vertical."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b88e096..966495a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A tela girará automaticamente."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A tela está bloqueada na orientação retrato."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index ea88ddd..141c13e 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -268,4 +268,6 @@
<skip />
<!-- no translation found for accessibility_rotation_lock_on_portrait (5809367521644012115) -->
<skip />
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c7dff3a..42991cb 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ecranul se va roti în mod automat."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ecranul este blocat în orientarea de tip peisaj."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ecranul este blocat în orientarea de tip portret."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index d435670..78b9297 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран будет поворачиваться автоматически."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Выбрана только альбомная ориентация экрана."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Выбрана только книжная ориентация экрана."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b57afe3..5fc943e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Obrazovka sa automaticky otočí."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamknutá v orientácii na šírku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamknutá v orientácii na výšku."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 1bed89b..9953428 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon se bo samodejno zasukal."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5dbbbb4..83ada24 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран ће се аутоматски ротирати."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран је закључан у хоризонталном положају."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран је закључан у вертикалном положају."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index f09dc2a..e87a2d9 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skärmen roteras automatiskt."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Bildskärmens riktning är nu låst i liggande format."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Bildskärmens riktning är nu låst i stående format."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index a6fec13..30fea17 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -58,7 +58,7 @@
<string name="always_use_accessory" msgid="1210954576979621596">"Tumia kama chaguo-msingi ya kifuasi hiki cha USB"</string>
<string name="usb_debugging_title" msgid="1114766024068112429">"Ruhusu Utatuaji USB?"</string>
<string name="usb_debugging_message" msgid="719863946976291180">"Ruhusu Utatuaji wa USB kutoka kwenye kompyuta hii?"\n"Kitufe chako RSA cha alama ya kidole ni "\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
- <string name="usb_debugging_always" msgid="4253099426793114693">"Kila wakati ruhusu kompyuta hii"</string>
+ <string name="usb_debugging_always" msgid="4253099426793114693">"Ruhusu kompyuta hii kila wakati"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Kuza ili kujaza skrini"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Tanua ili kujaza skrini"</string>
<string name="compat_mode_help_header" msgid="7969493989397529910">"Kukuza kwa Utangamanifu"</string>
@@ -147,4 +147,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrini itazunguka kiotomatiki."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrini imefungwa sasa katika uelekezo wa mandhari."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrini imefungwa katika uelekeo wa picha."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index de63d9f..bf01a8d 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -29,5 +29,8 @@
<!-- Whether recents thumbnails should stretch in both x and y to fill their
ImageView -->
<bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">0</integer>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c24fa36..89c640d 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -59,7 +59,7 @@
<string name="always_use_device" msgid="1450287437017315906">"ใช้ค่าเริ่มต้นสำหรับอุปกรณ์ USB นี้"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"ใช้ค่าเริ่มต้นสำหรับอุปกรณ์เสริม USB นี้"</string>
<string name="usb_debugging_title" msgid="1114766024068112429">"อนุญาตการแก้ไขข้อบกพร่องของ USB หรือไม่"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"อนุญาตการแก้ไขข้อบกพร่องของ USB จากคอมพิวเตอร์เครื่องนี้หรือไ่ม่"\n"ลายนิ้วมือคีย์ RSA ของคุณคือ"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"อนุญาตการแก้ไขข้อบกพร่องของ USB จากคอมพิวเตอร์เครื่องนี้หรือไม่"\n"ลายนิ้วมือคีย์ RSA ของคุณคือ"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"อนุญาตคอมพิวเตอร์เครื่องนี้เสมอ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ขยายจนเต็มหน้าจอ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ยืดจนเต็มหน้าจอ"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"หน้าจอจะหมุนโดยอัตโนมัติ"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index ad601c9..88d3607 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Awtomatikong iikot ang screen."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Naka-lock ang screen sa pahigang oryentasyon."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Naka-lock ang screen sa patayong oryentasyon."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c4c64d9..15bc353 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran otomatik olarak dönecektir."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 973c6bc..c731fad 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран обертатиметься автоматично."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран заблоковано в альбомній орієнтації."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран заблоковано в книжковій орієнтації."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c642c87..6a51cc1 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -59,7 +59,7 @@
<string name="always_use_device" msgid="1450287437017315906">"Sử dụng theo mặc định cho thiết bị USB này"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"Sử dụng theo mặc định cho phụ kiện USB này"</string>
<string name="usb_debugging_title" msgid="1114766024068112429">"Cho phép gỡ lỗi USB?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"Cho phép gỡ lỗi USB từ máy tính này?"\n"Tệp tham chiếu chính của RSA của bạn là"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"Cho phép gỡ lỗi USB từ máy tính này?"\n"Dấu tay khóa RSA của bạn là"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"Luôn cho phép máy tính này"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"T.phóng để lấp đầy m.hình"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Giãn ra để lấp đầy m.hình"</string>
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Màn hình sẽ xoay tự động."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 408f861..6aa03a0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"屏幕会自动旋转。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"屏幕锁定为纵向模式。"</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index debdb1c..053b538c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -151,4 +151,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"螢幕會自動旋轉。"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"螢幕已鎖定為橫向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"螢幕已鎖定為垂直模式。"</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index f31a11e..c10e968 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -149,4 +149,6 @@
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Isikrini sizophenduka ngokuzenzakalela."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
+ <!-- no translation found for jelly_bean_dream_name (5992026543636816792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 48fb21f..047570f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -35,5 +35,17 @@
<declare-styleable name="RecentsPanelView">
<attr name="recentItemLayout" format="reference" />
</declare-styleable>
+ <declare-styleable name="DeadZone">
+ <attr name="minSize" format="dimension" />
+ <attr name="maxSize" format="dimension" />
+ <attr name="holdTime" format="integer" />
+ <attr name="decayTime" format="integer" />
+ <attr name="orientation" />
+ </declare-styleable>
+
+ <attr name="orientation">
+ <enum name="horizontal" value="0" />
+ <enum name="vertical" value="1" />
+ </attr>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1cd7904..34e58a3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -72,5 +72,14 @@
<!-- Whether we're using the tablet-optimized recents interface (we use this
value at runtime for some things) -->
<integer name="status_bar_recents_bg_gradient_degrees">90</integer>
+
+ <!-- decay duration (from size_max -> size), in ms -->
+ <integer name="navigation_bar_deadzone_hold">333</integer>
+ <integer name="navigation_bar_deadzone_decay">333</integer>
+
+ <bool name="config_dead_zone_flash">false</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">3</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 94465e2..0d7cdb1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -63,6 +63,8 @@
<!-- thickness (height) of the dead zone at the top of the navigation bar,
reducing false presses on navbar buttons; approx 2mm -->
<dimen name="navigation_bar_deadzone_size">12dp</dimen>
+ <!-- size of the dead zone when touches have recently occurred elsewhere on screen -->
+ <dimen name="navigation_bar_deadzone_size_max">32dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5747f22..43070c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -372,9 +372,6 @@
<!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_clear_all">Clear all notifications.</string>
- <!-- Description of the desk dock action that invokes the Android Dreams screen saver feature -->
- <string name="dreams_dock_launcher">Activate screen saver</string>
-
<!-- Title shown in notification popup for inspecting the responsible
application -->
<string name="status_bar_notification_inspect_item_title">App info</string>
@@ -397,4 +394,11 @@
<!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is off. [CHAR LIMIT=NONE] -->
<string name="accessibility_rotation_lock_on_portrait">Screen is locked in portrait orientation.</string>
+
+ <!-- Name of the Jelly Bean platlogo screensaver -->
+ <string name="jelly_bean_dream_name">BeanFlinger</string>
+
+ <!-- Name of the launcher shortcut icon that allows dreams to be started immediately [CHAR LIMIT=20] -->
+ <string name="start_dreams">Start dreams</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
index 616d72f..f5a90ca 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBag.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -24,6 +24,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
@@ -40,6 +41,7 @@
import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -402,6 +404,11 @@
public void onStart() {
super.onStart();
+ // ACHIEVEMENT UNLOCKED
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(this, BeanBagDream.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2512c02..0671e44 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -30,10 +30,8 @@
import android.renderscript.Matrix4f;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
new file mode 100644
index 0000000..89d4ef7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -0,0 +1,61 @@
+/*);
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.dreams.IDreamManager;
+import android.util.Slog;
+
+public class Somnambulator extends Activity {
+
+ public Somnambulator() {
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Intent launchIntent = getIntent();
+ final String action = launchIntent.getAction();
+ if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
+ Intent shortcutIntent = new Intent(this, Somnambulator.class);
+ shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent resultIntent = new Intent();
+ resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_dreams));
+ resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams));
+ setResult(RESULT_OK, resultIntent);
+ } else {
+ IDreamManager somnambulist = IDreamManager.Stub.asInterface(
+ ServiceManager.checkService("dreams"));
+ if (somnambulist != null) {
+ try {
+ Slog.v("Somnambulator", "Dreaming by user request.");
+ somnambulist.dream();
+ } catch (RemoteException e) {
+ // fine, stay asleep then
+ }
+ }
+ }
+ finish();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 4281ccf..6d84350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,7 +30,6 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Process;
-import android.os.UserHandle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -254,10 +253,9 @@
public boolean onTouch(View v, MotionEvent ev) {
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
- mHandler.post(mPreloadTasksRunnable);
+ preloadRecentTasksList();
} else if (action == MotionEvent.ACTION_CANCEL) {
- cancelLoadingThumbnailsAndIcons();
- mHandler.removeCallbacks(mPreloadTasksRunnable);
+ cancelPreloadingRecentTasksList();
} else if (action == MotionEvent.ACTION_UP) {
// Remove the preloader if we haven't called it yet
mHandler.removeCallbacks(mPreloadTasksRunnable);
@@ -269,6 +267,15 @@
return false;
}
+ public void preloadRecentTasksList() {
+ mHandler.post(mPreloadTasksRunnable);
+ }
+
+ public void cancelPreloadingRecentTasksList() {
+ cancelLoadingThumbnailsAndIcons();
+ mHandler.removeCallbacks(mPreloadTasksRunnable);
+ }
+
public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
// Only oblige this request if it comes from the current RecentsPanel
// (eg when you rotate, the old RecentsPanel request should be ignored)
@@ -337,8 +344,7 @@
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasksForUser(MAX_TASKS,
- ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.USER_CURRENT);
+ am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
int numTasks = recentTasks.size();
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index a4c8e64..baacde0 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -136,6 +136,8 @@
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(mRecentsPanel));
mRecentsPanel.setRecentTasksLoader(recentTasksLoader);
recentTasksLoader.setRecentsPanel(mRecentsPanel, mRecentsPanel);
+ mRecentsPanel.setMinSwipeAlpha(
+ getResources().getInteger(R.integer.config_recent_item_min_alpha) / 100f);
handleIntent(getIntent());
mIntentFilter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index c3ecdb5..730d350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -38,15 +38,11 @@
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Display;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
@@ -60,7 +56,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
import com.android.systemui.statusbar.tablet.TabletStatusBar;
@@ -72,7 +67,6 @@
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
private Context mContext;
- private BaseStatusBar mBar;
private PopupMenu mPopup;
private View mRecentsScrim;
private View mRecentsNoApps;
@@ -164,13 +158,6 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = createView(parent);
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
- } else {
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
}
ViewHolder holder = (ViewHolder) convertView.getTag();
@@ -366,11 +353,6 @@
return mShowing;
}
- public void setBar(BaseStatusBar bar) {
- mBar = bar;
-
- }
-
public void setStatusBarView(View statusBarView) {
if (mStatusBarTouchProxy != null) {
mStatusBarTouchProxy.setStatusBar(statusBarView);
@@ -600,6 +582,9 @@
usingDrawingCache = true;
}
+ if (bm == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
holder.thumbnailViewImage, bm, 0, 0, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 106ce7e..97034bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -488,11 +488,18 @@
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
float thumbHeight = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
true);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
}
+
DisplayMetrics dm = new DisplayMetrics();
mDisplay.getMetrics(dm);
// calculate it here, but consider moving it elsewhere
@@ -521,8 +528,7 @@
+ thumbBgPadding + thumbLeftMargin);
y = (int) (dm.heightPixels
- res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
- } else { // if (config.orientation ==
- // Configuration.ORIENTATION_LANDSCAPE) {
+ } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
float thumbTopMargin = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
float thumbBgPadding = res
@@ -588,25 +594,11 @@
break;
case MSG_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.preloadRecentTasksList();
- //}
- }
+ getRecentTasksLoader().preloadRecentTasksList();
break;
case MSG_CANCEL_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "cancel preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.clearRecentTasksList();
- //}
- }
+ getRecentTasksLoader().cancelPreloadingRecentTasksList();
break;
case MSG_OPEN_SEARCH_PANEL:
if (DEBUG) Slog.d(TAG, "opening search panel");
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 33973b6..dcc2e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -45,13 +45,12 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
+import com.android.systemui.statusbar.policy.DeadZone;
public class NavigationBarView extends LinearLayout {
final static boolean DEBUG = false;
final static String TAG = "PhoneStatusBar/NavigationBarView";
- final static boolean DEBUG_DEADZONE = false;
-
final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen
@@ -71,6 +70,7 @@
private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
private DelegateViewHelper mDelegateHelper;
+ private DeadZone mDeadZone;
// workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -109,10 +109,14 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mDelegateHelper != null) {
- mDelegateHelper.onInterceptTouchEvent(event);
+ if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mDeadZone.poke(event);
}
- return true;
+ if (mDelegateHelper != null) {
+ boolean ret = mDelegateHelper.onInterceptTouchEvent(event);
+ if (ret) return true;
+ }
+ return super.onTouchEvent(event);
}
@Override
@@ -335,15 +339,13 @@
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
+ mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
+
// force the low profile & disabled states into compliance
setLowProfile(mLowProfile, false, true /* force */);
setDisabledFlags(mDisabledFlags, true /* force */);
setMenuVisibility(mShowMenu, true /* force */);
- if (DEBUG_DEADZONE) {
- mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF);
- }
-
if (DEBUG) {
Slog.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 33f467f..2f551e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -5,7 +5,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.Log;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -18,7 +18,7 @@
public static final String TAG = PanelView.class.getSimpleName();
public final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
- Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+ Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
public static final boolean BRAKES = false;
@@ -175,6 +175,12 @@
event.offsetLocation(-deltaX, -deltaY);
}
+ // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mHandleView.dispatchTouchEvent(event);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
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 9d2678a..5646c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -109,8 +109,6 @@
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
- private static final boolean SHOW_CARRIER_LABEL = false; // XXX: doesn't work with rubberband panels right now
-
private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
// 1020-1030 reserved for BaseStatusBar
@@ -189,6 +187,9 @@
private boolean mCarrierLabelVisible = false;
private int mCarrierLabelHeight;
private TextView mEmergencyCallLabel;
+ private int mNotificationHeaderHeight;
+
+ private boolean mShowCarrierInPanel = false;
// position
int[] mPositionTmp = new int[2];
@@ -310,14 +311,6 @@
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
- // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
- View.OnTouchListener clickStopper = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return true;
- }
- };
- mNotificationPanel.setOnTouchListener(clickStopper);
mNotificationPanelIsFullScreenWidth =
(mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
mNotificationPanel.setSystemUiVisibility(
@@ -326,7 +319,6 @@
// quick settings (WIP)
mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
- mSettingsPanel.setOnTouchListener(clickStopper);
if (!ActivityManager.isHighEndGfx()) {
mStatusBarWindow.setBackground(null);
@@ -419,8 +411,10 @@
}});
}
- if (SHOW_CARRIER_LABEL) {
- mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mShowCarrierInPanel = (mCarrierLabel != null);
+ Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
+ if (mShowCarrierInPanel) {
mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
// for mobile devices, we always show mobile connection info here (SPN/PLMN)
@@ -451,6 +445,7 @@
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
context.registerReceiver(mBroadcastReceiver, filter);
return mStatusBarView;
@@ -509,19 +504,6 @@
return lp;
}
- /*
- protected void updateRecentsPanel() {
- super.updateRecentsPanel(R.layout.status_bar_recent_panel);
- // Make .03 alpha the minimum so you always see the item a bit-- slightly below
- // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
- // a bit jarring
- mRecentsPanel.setMinSwipeAlpha(0.03f);
- if (mNavigationBarView != null) {
- mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
- }
- }
- */
-
@Override
protected void updateSearchPanel() {
super.updateSearchPanel();
@@ -639,6 +621,7 @@
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
// this will allow the navbar to run in an overlay on devices that support this
@@ -744,6 +727,10 @@
*/
if (notification.notification.fullScreenIntent != null) {
+ // Stop screensaver if the notification has a full-screen intent.
+ // (like an incoming phone call)
+ awakenDreams();
+
// not immersive & a full-screen alert should be shown
Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
@@ -787,11 +774,6 @@
setAreThereNotifications();
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- updateShowSearchHoldoff();
- }
-
private void updateShowSearchHoldoff() {
mShowSearchHoldoff = mContext.getResources().getInteger(
R.integer.config_show_search_delay);
@@ -883,7 +865,7 @@
}
protected void updateCarrierLabelVisibility(boolean force) {
- if (!SHOW_CARRIER_LABEL) return;
+ if (!mShowCarrierInPanel) return;
// The idea here is to only show the carrier label when there is enough room to see it,
// i.e. when there aren't enough notifications to fill the panel.
if (DEBUG) {
@@ -894,7 +876,7 @@
final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
final boolean makeVisible =
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
- && mPile.getHeight() < (mScrollView.getHeight() - mCarrierLabelHeight);
+ && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
@@ -1647,6 +1629,8 @@
lp.gravity = mSettingsPanelGravity;
lp.rightMargin = mNotificationPanelMarginPx;
mSettingsPanel.setLayoutParams(lp);
+
+ updateCarrierLabelVisibility(false);
}
// called by makeStatusbar and also by PhoneStatusBarView
@@ -1803,9 +1787,17 @@
makeExpandedInvisible();
}
else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ if (DEBUG) {
+ Slog.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+ }
updateResources();
repositionNavigationBar();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ updateShowSearchHoldoff();
+ }
+ else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
+ repositionNavigationBar();
}
}
};
@@ -1913,6 +1905,7 @@
+ res.getDimensionPixelSize(R.dimen.close_handle_underlap);
mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
+ mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
if (false) Slog.v(TAG, "updateResources");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java
index 0d2538d..edad370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AirplaneModeController.java
@@ -24,6 +24,7 @@
import android.os.AsyncTask;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.widget.CompoundButton;
@@ -88,7 +89,7 @@
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("state", enabled);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 19fbe96..e5ef5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -16,26 +16,150 @@
package com.android.systemui.statusbar.policy;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
import com.android.systemui.R;
public class DeadZone extends View {
+ public static final String TAG = "DeadZone";
+
+ public static final boolean DEBUG = false;
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
+ private static final boolean CHATTY = true; // print to logcat when we eat a click
+
+ private boolean mShouldFlash;
+ private float mFlashFrac = 0f;
+
+ private int mSizeMax;
+ private int mSizeMin;
+ // Upon activity elsewhere in the UI, the dead zone will hold steady for
+ // mHold ms, then move back over the course of mDecay ms
+ private int mHold, mDecay;
+ private boolean mVertical;
+ private long mLastPokeTime;
+
+ private final Runnable mDebugFlash = new Runnable() {
+ @Override
+ public void run() {
+ ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+ }
+ };
+
public DeadZone(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeadZone(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DeadZone,
+ defStyle, 0);
+
+ mHold = a.getInteger(R.styleable.DeadZone_holdTime, 0);
+ mDecay = a.getInteger(R.styleable.DeadZone_decayTime, 0);
+
+ mSizeMin = a.getDimensionPixelSize(R.styleable.DeadZone_minSize, 0);
+ mSizeMax = a.getDimensionPixelSize(R.styleable.DeadZone_maxSize, 0);
+
+ int index = a.getInt(R.styleable.DeadZone_orientation, -1);
+ mVertical = (index == VERTICAL);
+
+ if (DEBUG)
+ Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ + (mVertical ? " vertical" : " horizontal"));
+
+ setFlashOnTouchCapture(context.getResources().getBoolean(R.bool.config_dead_zone_flash));
}
- // I made you a touch event
+ static float lerp(float a, float b, float f) {
+ return (b - a) * f + a;
+ }
+
+ private float getSize(long now) {
+ if (mSizeMax == 0)
+ return 0;
+ long dt = (now - mLastPokeTime);
+ if (dt > mHold + mDecay)
+ return mSizeMin;
+ if (dt < mHold)
+ return mSizeMax;
+ return (int) lerp(mSizeMax, mSizeMin, (float) (dt - mHold) / mDecay);
+ }
+
+ public void setFlashOnTouchCapture(boolean dbg) {
+ mShouldFlash = dbg;
+ mFlashFrac = 0f;
+ postInvalidate();
+ }
+
+ // I made you a touch event...
@Override
- public boolean onTouchEvent (MotionEvent event) {
- return true; // but I eated it
+ public boolean onTouchEvent(MotionEvent event) {
+ if (DEBUG) {
+ Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
+ }
+
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_OUTSIDE) {
+ poke(event);
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ if (DEBUG) {
+ Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
+ }
+ int size = (int) getSize(event.getEventTime());
+ if ((mVertical && event.getX() < size) || event.getY() < size) {
+ if (CHATTY) {
+ Slog.v(TAG, "consuming errant click: (" + event.getX() + "," + event.getY() + ")");
+ }
+ if (mShouldFlash) {
+ post(mDebugFlash);
+ postInvalidate();
+ }
+ return true; // ...but I eated it
+ }
+ }
+ return false;
+ }
+
+ public void poke(MotionEvent event) {
+ mLastPokeTime = event.getEventTime();
+ if (DEBUG)
+ Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
+ postInvalidate();
+ }
+
+ public void setFlash(float f) {
+ mFlashFrac = f;
+ postInvalidate();
+ }
+
+ public float getFlash() {
+ return mFlashFrac;
+ }
+
+ @Override
+ public void onDraw(Canvas can) {
+ if (!mShouldFlash || mFlashFrac <= 0f) {
+ return;
+ }
+
+ final int size = (int) getSize(SystemClock.uptimeMillis());
+ can.clipRect(0, 0, mVertical ? size : can.getWidth(), mVertical ? can.getHeight() : size);
+ final float frac = DEBUG ? (mFlashFrac - 0.5f) + 0.5f : mFlashFrac;
+ can.drawARGB((int) (frac * 0xFF), 0xDD, 0xEE, 0xAA);
+
+ if (DEBUG && size > mSizeMin)
+ // crazy aggressive redrawing here, for debugging only
+ postInvalidateDelayed(100);
}
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index a60bba7..bec5d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
@@ -84,10 +85,12 @@
}
try {
+ // XXX WHAT TO DO ABOUT MULTI-USER?
if (visible) {
Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
gpsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, gpsIntent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
+ gpsIntent, 0, null, UserHandle.CURRENT);
Notification n = new Notification.Builder(mContext)
.setSmallIcon(iconId)
@@ -108,11 +111,12 @@
null,
GPS_NOTIFICATION_ID,
n,
- idOut);
+ idOut,
+ UserHandle.USER_CURRENT);
} else {
- mNotificationService.cancelNotification(
- mContext.getPackageName(),
- GPS_NOTIFICATION_ID);
+ mNotificationService.cancelNotificationWithTag(
+ mContext.getPackageName(), null,
+ GPS_NOTIFICATION_ID, UserHandle.USER_CURRENT);
}
} catch (android.os.RemoteException ex) {
// well, it was worth a shot
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 8365d08..d94c6b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -988,6 +988,8 @@
combinedActivityIconId = mMobileActivityIconId;
combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
mContentDescriptionCombinedSignal = mContentDescriptionDataType;
+ } else {
+ mMobileActivityIconId = 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 8ca3a9c..7153ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -355,7 +355,7 @@
mWindowManager.addView(mCompatModePanel, lp);
- //mRecentButton.setOnTouchListener(mRecentsPanel); //TODO: plumb this
+ mRecentButton.setOnTouchListener(getRecentTasksLoader());
mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
mPile.removeAllViews();
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 8d19f34..10f45a5 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
@@ -867,7 +868,7 @@
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("state", on);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
if (!mHasTelephony) {
mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index b88d84b..b72bb2b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -26,8 +26,8 @@
import android.media.IAudioService;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.telephony.TelephonyManager;
-import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.view.View;
@@ -142,7 +142,8 @@
// Broadcast an intent that the Camera button was longpressed
Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- mContext.sendOrderedBroadcast(intent, null);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
+ null, null, null, 0, null, null);
}
return true;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 3e96f9b..e761847 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2841,7 +2841,7 @@
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
mDecor.setLayoutDirection(
- getContext().getResources().getConfiguration().layoutDirection);
+ getContext().getResources().getConfiguration().getLayoutDirection());
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 209ad38..54cf73a 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -40,7 +40,6 @@
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.IAudioService;
-import android.os.BatteryManager;
import android.os.Bundle;
import android.os.FactoryTest;
import android.os.Handler;
@@ -61,16 +60,18 @@
import com.android.internal.R;
import com.android.internal.policy.PolicyManager;
+import com.android.internal.policy.impl.keyguard.KeyguardViewManager;
+import com.android.internal.policy.impl.keyguard.KeyguardViewMediator;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.PointerLocationView;
-import android.service.dreams.IDreamManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -108,11 +109,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
@@ -218,14 +221,18 @@
static final int NAVIGATION_BAR_PANEL_LAYER = 20;
// system-level error dialogs
static final int SYSTEM_ERROR_LAYER = 21;
+ // used to highlight the magnified portion of a display
+ static final int MAGNIFICATION_OVERLAY_LAYER = 22;
+ // used to simulate secondary display devices
+ static final int DISPLAY_OVERLAY_LAYER = 23;
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
- static final int DRAG_LAYER = 22;
- static final int SECURE_SYSTEM_OVERLAY_LAYER = 23;
- static final int BOOT_PROGRESS_LAYER = 24;
+ static final int DRAG_LAYER = 24;
+ static final int SECURE_SYSTEM_OVERLAY_LAYER = 25;
+ static final int BOOT_PROGRESS_LAYER = 26;
// the (mouse) pointer layer
- static final int POINTER_LAYER = 25;
- static final int HIDDEN_NAV_CONSUMER_LAYER = 26;
+ static final int POINTER_LAYER = 27;
+ static final int HIDDEN_NAV_CONSUMER_LAYER = 28;
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -861,7 +868,7 @@
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
if (!mHeadless) {
// don't create KeyguardViewMediator if headless
- mKeyguardMediator = new KeyguardViewMediator(context, this);
+ mKeyguardMediator = new KeyguardViewMediator(context, null);
}
mHandler = new PolicyHandler();
mOrientationListener = new MyOrientationListener(mContext);
@@ -1327,6 +1334,10 @@
return SCREENSAVER_LAYER;
case TYPE_UNIVERSE_BACKGROUND:
return UNIVERSE_BACKGROUND_LAYER;
+ case TYPE_DISPLAY_OVERLAY:
+ return DISPLAY_OVERLAY_LAYER;
+ case TYPE_MAGNIFICATION_OVERLAY:
+ return MAGNIFICATION_OVERLAY_LAYER;
}
Log.e(TAG, "Unknown window type: " + type);
return APPLICATION_LAYER;
@@ -1813,7 +1824,8 @@
if (down && repeatCount == 0) {
if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
- mContext.sendOrderedBroadcast(intent, null);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
+ null, null, null, 0, null, null);
return -1;
} else if (SHOW_PROCESSES_ON_ALT_MENU &&
(metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
@@ -2231,6 +2243,7 @@
& ~mForceClearedSystemUiFlags;
}
+ @Override
public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
final int fl = attrs.flags;
final int systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility);
@@ -2271,7 +2284,9 @@
}
/** {@inheritDoc} */
- public void beginLayoutLw(int displayWidth, int displayHeight, int displayRotation) {
+ @Override
+ public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
+ int displayRotation) {
mUnrestrictedScreenLeft = mUnrestrictedScreenTop = 0;
mUnrestrictedScreenWidth = displayWidth;
mUnrestrictedScreenHeight = displayHeight;
@@ -2298,136 +2313,138 @@
pf.right = df.right = vf.right = mDockRight;
pf.bottom = df.bottom = vf.bottom = mDockBottom;
- // For purposes of putting out fake window up to steal focus, we will
- // drive nav being hidden only by whether it is requested.
- boolean navVisible = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+ if (isDefaultDisplay) {
+ // For purposes of putting out fake window up to steal focus, we will
+ // drive nav being hidden only by whether it is requested.
+ boolean navVisible = (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
- // When the navigation bar isn't visible, we put up a fake
- // input window to catch all touch events. This way we can
- // detect when the user presses anywhere to bring back the nav
- // bar and ensure the application doesn't see the event.
- if (navVisible) {
- if (mHideNavFakeWindow != null) {
- mHideNavFakeWindow.dismiss();
- mHideNavFakeWindow = null;
+ // When the navigation bar isn't visible, we put up a fake
+ // input window to catch all touch events. This way we can
+ // detect when the user presses anywhere to bring back the nav
+ // bar and ensure the application doesn't see the event.
+ if (navVisible) {
+ if (mHideNavFakeWindow != null) {
+ mHideNavFakeWindow.dismiss();
+ mHideNavFakeWindow = null;
+ }
+ } else if (mHideNavFakeWindow == null) {
+ mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
+ mHandler.getLooper(), mHideNavInputEventReceiverFactory,
+ "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER,
+ 0, false, false, true);
}
- } else if (mHideNavFakeWindow == null) {
- mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
- mHandler.getLooper(), mHideNavInputEventReceiverFactory,
- "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER,
- 0, false, false, true);
- }
- // For purposes of positioning and showing the nav bar, if we have
- // decided that it can't be hidden (because of the screen aspect ratio),
- // then take that into account.
- navVisible |= !mCanHideNavigationBar;
+ // For purposes of positioning and showing the nav bar, if we have
+ // decided that it can't be hidden (because of the screen aspect ratio),
+ // then take that into account.
+ navVisible |= !mCanHideNavigationBar;
- if (mNavigationBar != null) {
- // Force the navigation bar to its appropriate place and
- // size. We need to do this directly, instead of relying on
- // it to bubble up from the nav bar, because this needs to
- // change atomically with screen rotations.
- mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
- if (mNavigationBarOnBottom) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- int top = displayHeight - mNavigationBarHeightForRotation[displayRotation];
- mTmpNavigationFrame.set(0, top, displayWidth, displayHeight);
- mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
- if (navVisible) {
- mNavigationBar.showLw(true);
- mDockBottom = mTmpNavigationFrame.top;
- mRestrictedScreenHeight = mDockBottom - mDockTop;
+ if (mNavigationBar != null) {
+ // Force the navigation bar to its appropriate place and
+ // size. We need to do this directly, instead of relying on
+ // it to bubble up from the nav bar, because this needs to
+ // change atomically with screen rotations.
+ mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+ if (mNavigationBarOnBottom) {
+ // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+ int top = displayHeight - mNavigationBarHeightForRotation[displayRotation];
+ mTmpNavigationFrame.set(0, top, displayWidth, displayHeight);
+ mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
+ if (navVisible) {
+ mNavigationBar.showLw(true);
+ mDockBottom = mTmpNavigationFrame.top;
+ mRestrictedScreenHeight = mDockBottom - mDockTop;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBar.hideLw(true);
+ }
+ if (navVisible && !mNavigationBar.isAnimatingLw()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemBottom = mTmpNavigationFrame.top;
+ }
} else {
- // We currently want to hide the navigation UI.
- mNavigationBar.hideLw(true);
+ // Landscape screen; nav bar goes to the right.
+ int left = displayWidth - mNavigationBarWidthForRotation[displayRotation];
+ mTmpNavigationFrame.set(left, 0, displayWidth, displayHeight);
+ mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
+ if (navVisible) {
+ mNavigationBar.showLw(true);
+ mDockRight = mTmpNavigationFrame.left;
+ mRestrictedScreenWidth = mDockRight - mDockLeft;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBar.hideLw(true);
+ }
+ if (navVisible && !mNavigationBar.isAnimatingLw()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemRight = mTmpNavigationFrame.left;
+ }
}
- if (navVisible && !mNavigationBar.isAnimatingLw()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemBottom = mTmpNavigationFrame.top;
- }
- } else {
- // Landscape screen; nav bar goes to the right.
- int left = displayWidth - mNavigationBarWidthForRotation[displayRotation];
- mTmpNavigationFrame.set(left, 0, displayWidth, displayHeight);
- mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
- if (navVisible) {
- mNavigationBar.showLw(true);
- mDockRight = mTmpNavigationFrame.left;
- mRestrictedScreenWidth = mDockRight - mDockLeft;
- } else {
- // We currently want to hide the navigation UI.
- mNavigationBar.hideLw(true);
- }
- if (navVisible && !mNavigationBar.isAnimatingLw()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemRight = mTmpNavigationFrame.left;
- }
- }
- // Make sure the content and current rectangles are updated to
- // account for the restrictions from the navigation bar.
- mContentTop = mCurTop = mDockTop;
- mContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mCurLeft = mDockLeft;
- mContentRight = mCurRight = mDockRight;
- mStatusBarLayer = mNavigationBar.getSurfaceLayer();
- // And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, mTmpNavigationFrame);
- if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
- }
- if (DEBUG_LAYOUT) Log.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
- mDockLeft, mDockTop, mDockRight, mDockBottom));
-
- // decide where the status bar goes ahead of time
- if (mStatusBar != null) {
- // apply any navigation bar insets
- pf.left = df.left = mUnrestrictedScreenLeft;
- pf.top = df.top = mUnrestrictedScreenTop;
- pf.right = df.right = mUnrestrictedScreenWidth - mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = mUnrestrictedScreenHeight - mUnrestrictedScreenTop;
- vf.left = mStableLeft;
- vf.top = mStableTop;
- vf.right = mStableRight;
- vf.bottom = mStableBottom;
-
- mStatusBarLayer = mStatusBar.getSurfaceLayer();
-
- // Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf, df, vf, vf);
-
- // For layout, the status bar is always at the top with our fixed height.
- mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
- // If the status bar is hidden, we don't want to cause
- // windows behind it to scroll.
- if (mStatusBar.isVisibleLw()) {
- // Status bar may go away, so the screen area it occupies
- // is available to apps but just covering them when the
- // status bar is visible.
- mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
-
+ // Make sure the content and current rectangles are updated to
+ // account for the restrictions from the navigation bar.
mContentTop = mCurTop = mDockTop;
mContentBottom = mCurBottom = mDockBottom;
mContentLeft = mCurLeft = mDockLeft;
mContentRight = mCurRight = mDockRight;
-
- if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: " +
- String.format(
- "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
- mDockLeft, mDockTop, mDockRight, mDockBottom,
- mContentLeft, mContentTop, mContentRight, mContentBottom,
- mCurLeft, mCurTop, mCurRight, mCurBottom));
+ mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+ // And compute the final frame.
+ mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+ mTmpNavigationFrame, mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Log.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
}
- if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()) {
- // If the status bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ if (DEBUG_LAYOUT) Log.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
+ mDockLeft, mDockTop, mDockRight, mDockBottom));
+
+ // decide where the status bar goes ahead of time
+ if (mStatusBar != null) {
+ // apply any navigation bar insets
+ pf.left = df.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = mUnrestrictedScreenTop;
+ pf.right = df.right = mUnrestrictedScreenWidth - mUnrestrictedScreenLeft;
+ pf.bottom = df.bottom = mUnrestrictedScreenHeight - mUnrestrictedScreenTop;
+ vf.left = mStableLeft;
+ vf.top = mStableTop;
+ vf.right = mStableRight;
+ vf.bottom = mStableBottom;
+
+ mStatusBarLayer = mStatusBar.getSurfaceLayer();
+
+ // Let the status bar determine its size.
+ mStatusBar.computeFrameLw(pf, df, vf, vf);
+
+ // For layout, the status bar is always at the top with our fixed height.
+ mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+ // If the status bar is hidden, we don't want to cause
+ // windows behind it to scroll.
+ if (mStatusBar.isVisibleLw()) {
+ // Status bar may go away, so the screen area it occupies
+ // is available to apps but just covering them when the
+ // status bar is visible.
+ mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+
+ mContentTop = mCurTop = mDockTop;
+ mContentBottom = mCurBottom = mDockBottom;
+ mContentLeft = mCurLeft = mDockLeft;
+ mContentRight = mCurRight = mDockRight;
+
+ if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: " +
+ String.format(
+ "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
+ mDockLeft, mDockTop, mDockRight, mDockBottom,
+ mContentLeft, mContentTop, mContentRight, mContentBottom,
+ mCurLeft, mCurTop, mCurRight, mCurBottom));
+ }
+ if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()) {
+ // If the status bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ }
}
}
}
@@ -2510,13 +2527,15 @@
}
/** {@inheritDoc} */
+ @Override
public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached) {
// we've already done the status bar
if (win == mStatusBar || win == mNavigationBar) {
return;
}
- final boolean needsToOffsetInputMethodTarget =
+ final boolean isDefaultDisplay = win.isDefaultDisplay();
+ final boolean needsToOffsetInputMethodTarget = isDefaultDisplay &&
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) {
@@ -2533,11 +2552,25 @@
final Rect df = mTmpDisplayFrame;
final Rect cf = mTmpContentFrame;
final Rect vf = mTmpVisibleFrame;
-
- final boolean hasNavBar = (mHasNavigationBar
+
+ final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
- if (attrs.type == TYPE_INPUT_METHOD) {
+ if (!isDefaultDisplay) {
+ if (attached != null) {
+ // If this window is attached to another, our display
+ // frame is the same as the one we are attached to.
+ setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
+ } else {
+ // Give the window full screen.
+ pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = cf.top = mUnrestrictedScreenTop;
+ pf.right = df.right = cf.right
+ = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+ pf.bottom = df.bottom = cf.bottom
+ = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ }
+ } else if (attrs.type == TYPE_INPUT_METHOD) {
pf.left = df.left = cf.left = vf.left = mDockLeft;
pf.top = df.top = cf.top = vf.top = mDockTop;
pf.right = df.right = cf.right = vf.right = mDockRight;
@@ -2604,6 +2637,7 @@
pf.right = df.right = mRestrictedScreenLeft+mRestrictedScreenWidth;
pf.bottom = df.bottom = mRestrictedScreenTop+mRestrictedScreenHeight;
}
+
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
cf.left = mDockLeft;
cf.top = mDockTop;
@@ -2615,6 +2649,7 @@
cf.right = mContentRight;
cf.bottom = mContentBottom;
}
+
applyStableConstraints(sysUiFl, fl, cf);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
@@ -2698,7 +2733,9 @@
pf.bottom = df.bottom = cf.bottom
= mRestrictedScreenTop+mRestrictedScreenHeight;
}
+
applyStableConstraints(sysUiFl, fl, cf);
+
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
@@ -2755,7 +2792,7 @@
}
}
}
-
+
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
df.left = df.top = cf.left = cf.top = vf.left = vf.top = -10000;
df.right = df.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
@@ -2767,9 +2804,9 @@
+ String.format(" flags=0x%08x", fl)
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString());
-
+
win.computeFrameLw(pf, df, cf, vf);
-
+
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (attrs.type == TYPE_INPUT_METHOD && !win.getGivenInsetsPendingLw()) {
@@ -2801,7 +2838,7 @@
}
/** {@inheritDoc} */
- public void beginAnimationLw(int displayWidth, int displayHeight) {
+ public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mForceStatusBar = false;
@@ -2811,7 +2848,7 @@
}
/** {@inheritDoc} */
- public void animatingWindowLw(WindowState win,
+ public void applyPostLayoutPolicyLw(WindowState win,
WindowManager.LayoutParams attrs) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
+ win.isVisibleOrBehindKeyguardLw());
@@ -2843,7 +2880,7 @@
}
/** {@inheritDoc} */
- public int finishAnimationLw() {
+ public int finishPostLayoutPolicyLw() {
int changes = 0;
boolean topIsFullscreen = false;
@@ -2898,10 +2935,11 @@
mTopIsFullscreen = topIsFullscreen;
- // Hide the key guard if a visible window explicitly specifies that it wants to be displayed
- // when the screen is locked
+ // Hide the key guard if a visible window explicitly specifies that it wants to be
+ // displayed when the screen is locked.
if (mKeyguard != null) {
- if (localLOGV) Log.v(TAG, "finishAnimationLw::mHideKeyguard="+mHideLockScreen);
+ if (localLOGV) Log.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ + mHideLockScreen);
if (mDismissKeyguard && !mKeyguardMediator.isSecure()) {
if (mKeyguard.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
@@ -2996,7 +3034,7 @@
Intent intent = new Intent(ACTION_HDMI_PLUGGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
}
@@ -3134,7 +3172,8 @@
@Override
public void onServiceDisconnected(ComponentName name) {}
};
- if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+ if (mContext.bindService(
+ intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
@@ -4277,6 +4316,18 @@
mLastInputMethodTargetWindow = target;
}
+ public boolean canMagnifyWindow(WindowManager.LayoutParams attrs) {
+ switch (attrs.type) {
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+ case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
+ return false;
+ }
+ }
+ return true;
+ }
+
public void dump(String prefix, PrintWriter pw, String[] args) {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
diff --git a/policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java
similarity index 98%
rename from policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java
rename to policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java
index f476f82..39afaa2 100644
--- a/policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import android.view.View;
diff --git a/policy/src/com/android/internal/policy/impl/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
similarity index 89%
copy from policy/src/com/android/internal/policy/impl/FaceUnlock.java
copy to policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index fda3c9d..5a0a228 100644
--- a/policy/src/com/android/internal/policy/impl/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
-import com.android.internal.R;
import com.android.internal.policy.IFaceLockCallback;
import com.android.internal.policy.IFaceLockInterface;
import com.android.internal.widget.LockPatternUtils;
@@ -31,7 +30,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
@@ -42,7 +40,6 @@
private final Context mContext;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardUpdateMonitor mUpdateMonitor;
// TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
private boolean mServiceRunning = false;
@@ -78,21 +75,22 @@
// Unlock
private final int BACKUP_LOCK_TIMEOUT = 5000;
- KeyguardScreenCallback mKeyguardScreenCallback;
+ KeyguardSecurityCallback mKeyguardScreenCallback;
/**
* Stores some of the structures that Face Unlock will need to access and creates the handler
* will be used to execute messages on the UI thread.
*/
- public FaceUnlock(Context context, KeyguardUpdateMonitor updateMonitor,
- LockPatternUtils lockPatternUtils, KeyguardScreenCallback keyguardScreenCallback) {
+ public FaceUnlock(Context context) {
mContext = context;
- mUpdateMonitor = updateMonitor;
- mLockPatternUtils = lockPatternUtils;
- mKeyguardScreenCallback = keyguardScreenCallback;
+ mLockPatternUtils = new LockPatternUtils(context);
mHandler = new Handler(this);
}
+ public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
+ mKeyguardScreenCallback = keyguardScreenCallback;
+ }
+
/**
* Stores and displays the view that Face Unlock is allowed to draw within.
* TODO: since the layout object will eventually be shared by multiple biometric unlock
@@ -119,11 +117,7 @@
if (mHandler.getLooper() != Looper.myLooper()) {
Log.e(TAG, "show() called off of the UI thread");
}
-
removeDisplayMessages();
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- }
if (timeoutMillis > 0) {
mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis);
}
@@ -234,7 +228,6 @@
* Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically
* come from the UI thread. This makes sure there are no race conditions between those calls.
*/
- @Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_SHOW_FACE_UNLOCK_VIEW:
@@ -276,23 +269,15 @@
*/
void handleShowFaceUnlockView() {
if (DEBUG) Log.d(TAG, "handleShowFaceUnlockView()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleShowFaceUnlockView()");
- }
+ // Not required
}
/**
- * Sets the Face Unlock view to invisible, thus exposing the backup lock.
+ * Hide face unlock and show backup
*/
void handleHideFaceUnlockView() {
if (DEBUG) Log.d(TAG, "handleHideFaceUnlockView()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleHideFaceUnlockView()");
- }
+ mKeyguardScreenCallback.showBackupSecurity();
}
/**
@@ -328,7 +313,7 @@
// When switching between portrait and landscape view while Face Unlock is running,
// the screen will eventually go dark unless we poke the wakelock when Face Unlock
// is restarted.
- mKeyguardScreenCallback.pokeWakelock();
+ mKeyguardScreenCallback.userActivity(0);
int[] position;
position = new int[2];
@@ -364,14 +349,9 @@
void handleUnlock() {
if (DEBUG) Log.d(TAG, "handleUnlock()");
removeDisplayMessages();
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleUnlock()");
- }
stop();
- mKeyguardScreenCallback.keyguardDone(true);
mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
+ mKeyguardScreenCallback.dismiss(true);
}
/**
@@ -379,13 +359,9 @@
*/
void handleCancel() {
if (DEBUG) Log.d(TAG, "handleCancel()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleCancel()");
- }
+ mKeyguardScreenCallback.dismiss(false);
stop();
- mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
+ mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
}
/**
@@ -393,7 +369,7 @@
*/
void handleReportFailedAttempt() {
if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
- mUpdateMonitor.reportFailedBiometricUnlockAttempt();
+ mKeyguardScreenCallback.reportFailedUnlockAttempt();
}
/**
@@ -403,18 +379,14 @@
*/
void handleExposeFallback() {
if (DEBUG) Log.d(TAG, "handleExposeFallback()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleExposeFallback()");
- }
+ // No longer required because face unlock doesn't cover backup unlock.
}
/**
* Pokes the wakelock to keep the screen alive and active for a specific amount of time.
*/
void handlePokeWakelock(int millis) {
- mKeyguardScreenCallback.pokeWakelock(millis);
+ mKeyguardScreenCallback.userActivity(millis);
}
/**
@@ -433,7 +405,6 @@
/**
* Called when the Face Unlock service connects after calling bind().
*/
- @Override
public void onServiceConnected(ComponentName className, IBinder iservice) {
Log.d(TAG, "Connected to Face Unlock service");
mService = IFaceLockInterface.Stub.asInterface(iservice);
@@ -443,7 +414,6 @@
/**
* Called if the Face Unlock service unexpectedly disconnects. This indicates an error.
*/
- @Override
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Unexpected disconnect from Face Unlock service");
mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
@@ -505,7 +475,6 @@
/**
* Called when Face Unlock wants to grant access to the user.
*/
- @Override
public void unlock() {
if (DEBUG) Log.d(TAG, "unlock()");
mHandler.sendEmptyMessage(MSG_UNLOCK);
@@ -514,7 +483,6 @@
/**
* Called when Face Unlock wants to go to the backup.
*/
- @Override
public void cancel() {
if (DEBUG) Log.d(TAG, "cancel()");
mHandler.sendEmptyMessage(MSG_CANCEL);
@@ -523,7 +491,6 @@
/**
* Called when Face Unlock wants to increment the number of failed attempts.
*/
- @Override
public void reportFailedAttempt() {
if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
@@ -534,7 +501,6 @@
* unlock can be exposed because the Face Unlock service is now covering the backup with its
* UI.
**/
- @Override
public void exposeFallback() {
if (DEBUG) Log.d(TAG, "exposeFallback()");
mHandler.sendEmptyMessage(MSG_EXPOSE_FALLBACK);
diff --git a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
similarity index 77%
copy from policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
index a4baeed..1e73c5b 100644
--- a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 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.
@@ -13,37 +13,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package com.android.internal.policy.impl;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
+package com.android.internal.policy.impl.keyguard;
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.accounts.OperationCanceledException;
+import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
-import android.accounts.AccountManagerCallback;
+import android.accounts.OperationCanceledException;
+import android.app.Dialog;
+import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.LoginFilter;
import android.text.TextWatcher;
+import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.os.Bundle;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.R;
import java.io.IOException;
@@ -51,53 +48,42 @@
* When the user forgets their password a bunch of times, we fall back on their
* account's login/password to unlock the phone (and reset their lock pattern).
*/
-public class AccountUnlockScreen extends RelativeLayout implements KeyguardScreen,
+public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView,
View.OnClickListener, TextWatcher {
+ private static final int AWAKE_POKE_MILLIS = 30000;
private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric";
- /**
- * The amount of millis to stay awake once this screen detects activity
- */
- private static final int AWAKE_POKE_MILLIS = 30000;
-
- private KeyguardScreenCallback mCallback;
+ private KeyguardSecurityCallback mCallback;
private LockPatternUtils mLockPatternUtils;
- private KeyguardUpdateMonitor mUpdateMonitor;
-
- private TextView mTopHeader;
- private TextView mInstructions;
private EditText mLogin;
private EditText mPassword;
private Button mOk;
+ public boolean mEnableFallback;
+ private KeyguardNavigationManager mNavigationManager;
/**
* Shown while making asynchronous check of password.
*/
private ProgressDialog mCheckingDialog;
- private KeyguardStatusViewManager mKeyguardStatusViewManager;
- /**
- * AccountUnlockScreen constructor.
- * @param configuration
- * @param updateMonitor
- */
- public AccountUnlockScreen(Context context, Configuration configuration,
- KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
- LockPatternUtils lockPatternUtils) {
- super(context);
- mCallback = callback;
- mLockPatternUtils = lockPatternUtils;
+ public KeyguardAccountView(Context context) {
+ this(context, null, 0);
+ }
- LayoutInflater.from(context).inflate(
- R.layout.keyguard_screen_glogin_unlock, this, true);
+ public KeyguardAccountView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
- mTopHeader = (TextView) findViewById(R.id.topHeader);
- mTopHeader.setText(mLockPatternUtils.isPermanentlyLocked() ?
- R.string.lockscreen_glogin_too_many_attempts :
- R.string.lockscreen_glogin_forgot_pattern);
+ public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
- mInstructions = (TextView) findViewById(R.id.instructions);
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mNavigationManager = new KeyguardNavigationManager(this);
mLogin = (EditText) findViewById(R.id.login);
mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
@@ -108,13 +94,22 @@
mOk = (Button) findViewById(R.id.ok);
mOk.setOnClickListener(this);
-
- mUpdateMonitor = updateMonitor;
-
- mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, updateMonitor,
- lockPatternUtils, callback, true);
+ reset();
}
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+
public void afterTextChanged(Editable s) {
}
@@ -122,7 +117,9 @@
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
- mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+ if (mCallback != null) {
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
+ }
}
@Override
@@ -132,23 +129,17 @@
return mLogin.requestFocus(direction, previouslyFocusedRect);
}
- /** {@inheritDoc} */
public boolean needsInput() {
return true;
}
- /** {@inheritDoc} */
- public void onPause() {
- mKeyguardStatusViewManager.onPause();
- }
-
- /** {@inheritDoc} */
- public void onResume() {
+ public void reset() {
// start fresh
mLogin.setText("");
mPassword.setText("");
mLogin.requestFocus();
- mKeyguardStatusViewManager.onResume();
+ mNavigationManager.setMessage(mLockPatternUtils.isPermanentlyLocked() ?
+ R.string.kg_login_too_many_attempts : R.string.kg_login_instructions);
}
/** {@inheritDoc} */
@@ -156,15 +147,12 @@
if (mCheckingDialog != null) {
mCheckingDialog.hide();
}
- mUpdateMonitor.removeCallback(this); // this must be first
mCallback = null;
mLockPatternUtils = null;
- mUpdateMonitor = null;
}
- /** {@inheritDoc} */
public void onClick(View v) {
- mCallback.pokeWakelock();
+ mCallback.userActivity(0);
if (v == mOk) {
asyncCheckPassword();
}
@@ -188,10 +176,10 @@
mContext.startActivity(intent);
mCallback.reportSuccessfulUnlockAttempt();
- // close the keyguard
- mCallback.keyguardDone(true);
+ // dismiss keyguard
+ mCallback.dismiss(true);
} else {
- mInstructions.setText(R.string.lockscreen_glogin_invalid_input);
+ mNavigationManager.setMessage(R.string.kg_login_invalid_input);
mPassword.setText("");
mCallback.reportFailedUnlockAttempt();
}
@@ -204,9 +192,9 @@
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mLockPatternUtils.isPermanentlyLocked()) {
- mCallback.goToLockScreen();
+ mCallback.dismiss(false);
} else {
- mCallback.forgotPattern(false);
+ // TODO: mCallback.forgotPattern(false);
}
return true;
}
@@ -266,7 +254,7 @@
}
private void asyncCheckPassword() {
- mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
final String login = mLogin.getText().toString();
final String password = mPassword.getText().toString();
Account account = findIntendedAccount(login);
@@ -281,7 +269,7 @@
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
- mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+ mCallback.userActivity(AWAKE_POKE_MILLIS);
final Bundle result = future.getResult();
final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
postOnCheckPasswordResult(verified);
@@ -306,7 +294,7 @@
if (mCheckingDialog == null) {
mCheckingDialog = new ProgressDialog(mContext);
mCheckingDialog.setMessage(
- mContext.getString(R.string.lockscreen_glogin_checking_password));
+ mContext.getString(R.string.kg_login_checking_password));
mCheckingDialog.setIndeterminate(true);
mCheckingDialog.setCancelable(false);
mCheckingDialog.getWindow().setType(
@@ -314,4 +302,16 @@
}
return mCheckingDialog;
}
+
+ @Override
+ public void onPause() {
+
+ }
+
+ @Override
+ public void onResume() {
+ reset();
+ }
+
}
+
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
new file mode 100644
index 0000000..7dffca8
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.BiometricSensorUnlock;
+import com.android.internal.policy.impl.keyguard.FaceUnlock;
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView {
+
+ private static final String TAG = "KeyguardFaceUnlockView";
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private LockPatternUtils mLockPatternUtils;
+ private BiometricSensorUnlock mBiometricUnlock;
+ private KeyguardNavigationManager mNavigationManager;
+ private View mFaceUnlockAreaView;
+
+ public KeyguardFaceUnlockView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardFaceUnlockView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mNavigationManager = new KeyguardNavigationManager(this);
+
+ initializeBiometricUnlockView();
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mKeyguardSecurityCallback = callback;
+ // TODO: formalize this in the interface or factor it out
+ ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback);
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ public void onPause() {
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.hide();
+ mBiometricUnlock.stop();
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
+ }
+
+ @Override
+ public void onResume() {
+ maybeStartBiometricUnlock();
+ mBiometricUnlock.show(0);
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mKeyguardSecurityCallback;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mBiometricUnlock.initializeView(mFaceUnlockAreaView);
+ }
+
+ private void initializeBiometricUnlockView() {
+ mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
+ if (mFaceUnlockAreaView != null) {
+ mBiometricUnlock = new FaceUnlock(mContext);
+ } else {
+ Log.w(TAG, "Couldn't find biometric unlock view");
+ }
+ }
+
+ /**
+ * Starts the biometric unlock if it should be started based on a number of factors including
+ * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
+ * unlock area.
+ */
+ private void maybeStartBiometricUnlock() {
+ if (mBiometricUnlock != null) {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final boolean backupIsTimedOut = (
+ monitor.getFailedUnlockAttempts() >=
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
+ if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
+ && !monitor.getMaxBiometricUnlockAttemptsReached()
+ && !backupIsTimedOut) {
+ mBiometricUnlock.start();
+ } else {
+ mBiometricUnlock.hide();
+ }
+ }
+ }
+
+ KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+ // We need to stop the biometric unlock when a phone call comes in
+ @Override
+ public void onPhoneStateChanged(int phoneState) {
+ if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
+ mBiometricUnlock.stop();
+ mBiometricUnlock.hide();
+ }
+ }
+
+ @Override
+ public void onUserSwitched(int userId) {
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ }
+ mLockPatternUtils.setCurrentUser(userId);
+ }
+ };
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
new file mode 100644
index 0000000..00bc9be
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.os.UserManager;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.RemoteViews.OnClickHandler;
+import android.widget.ViewFlipper;
+
+import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.internal.widget.LockPatternUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeyguardHostView extends KeyguardViewBase {
+ // Use this to debug all of keyguard
+ public static boolean DEBUG;
+
+ static final int APPWIDGET_HOST_ID = 0x4B455947;
+ private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
+
+ private static final String TAG = "KeyguardViewHost";
+
+ private static final int SECURITY_SELECTOR_ID = R.id.keyguard_selector_view;
+ private static final int SECURITY_PATTERN_ID = R.id.keyguard_pattern_view;
+ private static final int SECURITY_PASSWORD_ID = R.id.keyguard_password_view;
+ private static final int SECURITY_BIOMETRIC_ID = R.id.keyguard_face_unlock_view;
+ private static final int SECURITY_SIM_PIN_ID = R.id.keyguard_sim_pin_view;
+ private static final int SECURITY_SIM_PUK_ID = R.id.keyguard_sim_puk_view;
+ private static final int SECURITY_ACCOUNT_ID = R.id.keyguard_account_view;
+
+ private AppWidgetHost mAppWidgetHost;
+ private KeyguardWidgetPager mAppWidgetContainer;
+ private ViewFlipper mViewFlipper;
+ private boolean mEnableMenuKey;
+ private boolean mIsVerifyUnlockOnly;
+ private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
+ private int mCurrentSecurityId = SECURITY_SELECTOR_ID;
+
+ // KeyguardSecurityViews
+ final private int [] mViewIds = {
+ SECURITY_SELECTOR_ID,
+ SECURITY_PATTERN_ID,
+ SECURITY_PASSWORD_ID,
+ SECURITY_BIOMETRIC_ID,
+ SECURITY_SIM_PIN_ID,
+ SECURITY_SIM_PUK_ID,
+ SECURITY_ACCOUNT_ID,
+ };
+
+ private ArrayList<View> mViews = new ArrayList<View>(mViewIds.length);
+
+ protected Runnable mLaunchRunnable;
+
+ protected int mFailedAttempts;
+ private LockPatternUtils mLockPatternUtils;
+
+ private KeyguardSecurityModel mSecurityModel;
+
+ public KeyguardHostView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardHostView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(context);
+ mAppWidgetHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID, mOnClickHandler);
+ mSecurityModel = new KeyguardSecurityModel(mContext);
+
+ // The following enables the MENU key to work for testing automation
+ mEnableMenuKey = shouldEnableMenuKey();
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ mViewMediatorCallback.keyguardDoneDrawing();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
+ mAppWidgetContainer.setVisibility(VISIBLE);
+
+ // View Flipper
+ mViewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
+
+ // Initialize all security views
+ for (int i = 0; i < mViewIds.length; i++) {
+ View view = findViewById(mViewIds[i]);
+ mViews.add(view);
+ if (view != null) {
+ ((KeyguardSecurityView) view).setKeyguardCallback(mCallback);
+ } else {
+ Log.v("*********", "Can't find view id " + mViewIds[i]);
+ }
+ }
+ }
+
+ void setLockPatternUtils(LockPatternUtils utils) {
+ mSecurityModel.setLockPatternUtils(utils);
+ mLockPatternUtils = utils;
+ for (int i = 0; i < mViews.size(); i++) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) mViews.get(i);
+ if (ksv != null) {
+ ksv.setLockPatternUtils(utils);
+ } else {
+ Log.w(TAG, "**** ksv was null at " + i);
+ }
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAppWidgetHost.startListening();
+ maybePopulateWidgets();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mAppWidgetHost.stopListening();
+ }
+
+ AppWidgetHost getAppWidgetHost() {
+ return mAppWidgetHost;
+ }
+
+ void addWidget(AppWidgetHostView view) {
+ mAppWidgetContainer.addWidget(view);
+ }
+
+ private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
+
+ public void userActivity(long timeout) {
+ mViewMediatorCallback.pokeWakelock(timeout);
+ }
+
+ public void dismiss(boolean authenticated) {
+ showNextSecurityScreenOrFinish(authenticated);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return mIsVerifyUnlockOnly;
+ }
+
+ public void reportSuccessfulUnlockAttempt() {
+ KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
+ }
+
+ public void reportFailedUnlockAttempt() {
+ // TODO: handle biometric attempt differently.
+ KeyguardHostView.this.reportFailedUnlockAttempt();
+ }
+
+ public int getFailedAttempts() {
+ return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
+ }
+
+ @Override
+ public void showBackupSecurity() {
+ KeyguardHostView.this.showBackupSecurity();
+ }
+
+ @Override
+ public void setOnDismissRunnable(Runnable runnable) {
+ KeyguardHostView.this.setOnDismissRunnable(runnable);
+ }
+
+ };
+
+ private void showDialog(String title, String message) {
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(title)
+ .setMessage(message)
+ .setNeutralButton(com.android.internal.R.string.ok, null)
+ .create();
+ if (!(mContext instanceof Activity)) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ dialog.show();
+ }
+
+ private void showTimeoutDialog() {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ int messageId = 0;
+
+ switch (mSecurityModel.getSecurityMode()) {
+ case Pattern:
+ messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
+ break;
+
+ case Password: {
+ final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+ messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
+ : R.string.kg_too_many_failed_password_attempts_dialog_message;
+ }
+ break;
+ }
+
+ if (messageId != 0) {
+ final String message = mContext.getString(messageId,
+ KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
+ timeoutInSeconds);
+ showDialog(null, message);
+ }
+ }
+
+ private void showAlmostAtWipeDialog(int attempts, int remaining) {
+ int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
+ attempts, remaining);
+ showDialog(null, message);
+ }
+
+ private void showWipeDialog(int attempts) {
+ String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
+ showDialog(null, message);
+ }
+
+ private void showAlmostAtAccountLoginDialog() {
+ final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+ final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+ String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
+ count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
+ showDialog(null, message);
+ }
+
+ private void reportFailedUnlockAttempt() {
+ final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ SecurityMode mode = mSecurityModel.getSecurityMode();
+ final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
+
+ final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
+ .getMaximumFailedPasswordsForWipe(null);
+
+ final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+ - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
+ (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // If we reach this code, it means the user has installed a DevicePolicyManager
+ // that requests device wipe after N attempts. Once we get below the grace
+ // period, we'll post this dialog every time as a clear warning until the
+ // bombshell hits and the device is wiped.
+ if (remainingBeforeWipe > 0) {
+ showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
+ showWipeDialog(failedAttempts);
+ }
+ } else {
+ boolean showTimeout =
+ (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
+ if (usingPattern && mEnableFallback) {
+ if (failedAttempts == failedAttemptWarning) {
+ showAlmostAtAccountLoginDialog();
+ showTimeout = false; // don't show both dialogs
+ } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
+ mLockPatternUtils.setPermanentlyLocked(true);
+ showSecurityScreen(SECURITY_ACCOUNT_ID);
+ // don't show timeout dialog because we show account unlock screen next
+ showTimeout = false;
+ }
+ }
+ if (showTimeout) {
+ showTimeoutDialog();
+ }
+ }
+ monitor.reportFailedUnlockAttempt();
+ mLockPatternUtils.reportFailedPasswordAttempt();
+ }
+
+ /**
+ * Shows the backup security screen for the current security mode. This could be used for
+ * password recovery screens but is currently only used for pattern unlock to show the
+ * account unlock screen and biometric unlock to show the user's normal unlock.
+ */
+ private void showBackupSecurity() {
+ SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode());
+ SecurityMode backup = mSecurityModel.getBackupFor(currentMode);
+ showSecurityScreen(getSecurityViewIdForMode(backup));
+ }
+
+ private void showNextSecurityScreenOrFinish(boolean authenticated) {
+ boolean finish = false;
+ if (SECURITY_SELECTOR_ID == mCurrentSecurityId) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ // Allow an alternate, such as biometric unlock
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
+ int realSecurityId = getSecurityViewIdForMode(securityMode);
+ if (SECURITY_SELECTOR_ID == realSecurityId) {
+ finish = true; // no security required
+ } else {
+ showSecurityScreen(realSecurityId); // switch to the "real" security view
+ }
+ } else if (authenticated) {
+ switch (mCurrentSecurityId) {
+ case SECURITY_PATTERN_ID:
+ case SECURITY_PASSWORD_ID:
+ case SECURITY_ACCOUNT_ID:
+ case SECURITY_BIOMETRIC_ID:
+ finish = true;
+ break;
+
+ case SECURITY_SIM_PIN_ID:
+ case SECURITY_SIM_PUK_ID:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode != SecurityMode.None) {
+ showSecurityScreen(getSecurityViewIdForMode(securityMode));
+ } else {
+ finish = true;
+ }
+ break;
+
+ default:
+ showSecurityScreen(SECURITY_SELECTOR_ID);
+ break;
+ }
+ } else {
+ // Not authenticated but we were asked to dismiss so go back to selector screen.
+ showSecurityScreen(SECURITY_SELECTOR_ID);
+ }
+ if (finish) {
+ // If there's a pending runnable because the user interacted with a widget
+ // and we're leaving keyguard, then run it.
+ if (mLaunchRunnable != null) {
+ mLaunchRunnable.run();
+ mViewFlipper.setDisplayedChild(0);
+ mLaunchRunnable = null;
+ }
+ mViewMediatorCallback.keyguardDone(true);
+ }
+ }
+
+ private OnClickHandler mOnClickHandler = new OnClickHandler() {
+ @Override
+ public boolean onClickHandler(final View view,
+ final android.app.PendingIntent pendingIntent,
+ final Intent fillInIntent) {
+ if (pendingIntent.isActivity()) {
+ setOnDismissRunnable(new Runnable() {
+ public void run() {
+ try {
+ // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+ Context context = view.getContext();
+ ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
+ 0, 0,
+ view.getMeasuredWidth(), view.getMeasuredHeight());
+ context.startIntentSender(
+ pendingIntent.getIntentSender(), fillInIntent,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
+ } catch (IntentSender.SendIntentException e) {
+ android.util.Log.e(TAG, "Cannot send pending intent: ", e);
+ } catch (Exception e) {
+ android.util.Log.e(TAG, "Cannot send pending intent due to " +
+ "unknown exception: ", e);
+ }
+ }
+ });
+
+ mCallback.dismiss(false);
+ return true;
+ } else {
+ return super.onClickHandler(view, pendingIntent, fillInIntent);
+ }
+ };
+ };
+
+ @Override
+ public void reset() {
+ mIsVerifyUnlockOnly = false;
+ requestFocus();
+ }
+
+ /**
+ * Sets a runnable to run when keyguard is dismissed
+ * @param runnable
+ */
+ protected void setOnDismissRunnable(Runnable runnable) {
+ mLaunchRunnable = runnable;
+ }
+
+ private KeyguardSecurityView getSecurityView(int securitySelectorId) {
+ final int children = mViewFlipper.getChildCount();
+ for (int child = 0; child < children; child++) {
+ if (mViewFlipper.getChildAt(child).getId() == securitySelectorId) {
+ return ((KeyguardSecurityView)mViewFlipper.getChildAt(child));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityViewId
+ */
+ private void showSecurityScreen(int securityViewId) {
+
+ if (securityViewId == mCurrentSecurityId) return;
+
+ KeyguardSecurityView oldView = getSecurityView(mCurrentSecurityId);
+ KeyguardSecurityView newView = getSecurityView(securityViewId);
+
+ // Emulate Activity life cycle
+ oldView.onPause();
+ newView.onResume();
+
+ mViewMediatorCallback.setNeedsInput(newView.needsInput());
+
+ // Find and show this child.
+ final int childCount = mViewFlipper.getChildCount();
+
+ // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
+ final boolean doFlip = mCurrentSecurityId == SECURITY_SELECTOR_ID
+ || securityViewId == SECURITY_SELECTOR_ID;
+ final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
+ : R.anim.keyguard_security_fade_in;
+ final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
+ : R.anim.keyguard_security_fade_out;
+
+ mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
+ mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
+ for (int i = 0; i < childCount; i++) {
+ if (securityViewId == mViewFlipper.getChildAt(i).getId()) {
+ mViewFlipper.setDisplayedChild(i);
+ break;
+ }
+ }
+
+ // Discard current runnable if we're switching back to the selector view
+ if (securityViewId == SECURITY_SELECTOR_ID) {
+ setOnDismissRunnable(null);
+ }
+
+ mCurrentSecurityId = securityViewId;
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ if (DEBUG) Log.d(TAG, "screen on");
+ showSecurityScreen(mCurrentSecurityId);
+ getSecurityView(mCurrentSecurityId).onResume();
+ }
+
+ @Override
+ public void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, "screen off");
+ showSecurityScreen(SECURITY_SELECTOR_ID);
+ getSecurityView(mCurrentSecurityId).onPause();
+ }
+
+ @Override
+ public void show() {
+ onScreenTurnedOn();
+ }
+
+ private boolean isSecure() {
+ SecurityMode mode = mSecurityModel.getSecurityMode();
+ switch (mode) {
+ case Pattern:
+ return mLockPatternUtils.isLockPatternEnabled();
+ case Password:
+ return mLockPatternUtils.isLockPasswordEnabled();
+ case SimPin:
+ case SimPuk:
+ case Account:
+ return true;
+ case None:
+ return false;
+ default:
+ throw new IllegalStateException("Unknown security mode " + mode);
+ }
+ }
+
+ @Override
+ public void wakeWhenReadyTq(int keyCode) {
+ if (DEBUG) Log.d(TAG, "onWakeKey");
+ if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
+ if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
+ showSecurityScreen(SECURITY_SELECTOR_ID);
+ mViewMediatorCallback.pokeWakelock();
+ } else {
+ if (DEBUG) Log.d(TAG, "poking wake lock immediately");
+ mViewMediatorCallback.pokeWakelock();
+ }
+ }
+
+ @Override
+ public void verifyUnlock() {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
+ mViewMediatorCallback.keyguardDone(true);
+ } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
+ && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
+ // can only verify unlock when in pattern/password mode
+ mViewMediatorCallback.keyguardDone(false);
+ } else {
+ // otherwise, go to the unlock screen, see if they can verify it
+ mIsVerifyUnlockOnly = true;
+ showSecurityScreen(getSecurityViewIdForMode(securityMode));
+ }
+ }
+
+ private int getSecurityViewIdForMode(SecurityMode securityMode) {
+ switch (securityMode) {
+ case None: return SECURITY_SELECTOR_ID;
+ case Pattern: return SECURITY_PATTERN_ID;
+ case Password: return SECURITY_PASSWORD_ID;
+ case Biometric: return SECURITY_BIOMETRIC_ID;
+ case Account: return SECURITY_ACCOUNT_ID;
+ case SimPin: return SECURITY_SIM_PIN_ID;
+ case SimPuk: return SECURITY_SIM_PUK_ID;
+ }
+ return 0;
+ }
+
+ private void addWidget(int appId) {
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+ AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
+ AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
+ addWidget(view);
+ }
+
+ private void maybePopulateWidgets() {
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null && dpm.getKeyguardWidgetsDisabled(null)
+ != DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_NONE) {
+ Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
+ return;
+ }
+ inflateAndAddUserSelectorWidgetIfNecessary();
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ KEYGUARD_WIDGET_PREFS, Context.MODE_PRIVATE);
+ for (String key : prefs.getAll().keySet()) {
+ int appId = prefs.getInt(key, -1);
+ if (appId != -1) {
+ Log.w(TAG, "populate: adding " + key);
+ addWidget(appId);
+ } else {
+ Log.w(TAG, "populate: can't find " + key);
+ }
+ }
+ }
+
+ private void inflateAndAddUserSelectorWidgetIfNecessary() {
+ // if there are multiple users, we need to add the multi-user switcher widget to the
+ // keyguard.
+ UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ List<UserInfo> users = mUm.getUsers();
+
+ if (users.size() > 1) {
+ KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
+ LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
+ mAppWidgetContainer, false);
+ // add the switcher in the first position
+ mAppWidgetContainer.addView(userswitcher, 0);
+ }
+ }
+
+ @Override
+ public void cleanUp() {
+
+ }
+
+ /**
+ * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
+ * some cases where we wish to disable it, notably when the menu button placement or technology
+ * is prone to false positives.
+ *
+ * @return true if the menu key should be enabled
+ */
+ private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
+ private boolean shouldEnableMenuKey() {
+ final Resources res = getResources();
+ final boolean configDisabled = res.getBoolean(
+ com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
+ final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
+ final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
+ return !configDisabled || isTestHarness || fileOverride;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
+ showNextSecurityScreenOrFinish(false);
+ return true;
+ } else {
+ return super.onKeyDown(keyCode, event);
+ }
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
new file mode 100644
index 0000000..8aef68f
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+class KeyguardMultiUserAvatar extends FrameLayout {
+ private static final String TAG = "KeyguardViewHost";
+
+ private ImageView mUserImage;
+ private TextView mUserName;
+ private UserInfo mUserInfo;
+ private KeyguardMultiUserSelectorView mUserSelector;
+
+ public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
+ KeyguardMultiUserSelectorView userSelector, UserInfo info) {
+ KeyguardMultiUserAvatar icon = (KeyguardMultiUserAvatar)
+ LayoutInflater.from(context).inflate(resId, userSelector, false);
+
+ icon.setup(info, userSelector);
+ return icon;
+ }
+
+ public KeyguardMultiUserAvatar(Context context) {
+ super(context, null, 0);
+ }
+
+ public KeyguardMultiUserAvatar(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ public KeyguardMultiUserAvatar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setup(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
+ mUserInfo = user;
+ mUserSelector = userSelector;
+ init();
+ }
+
+ private void init() {
+ mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
+ mUserName = (TextView) findViewById(R.id.keyguard_user_name);
+
+ mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
+ mUserName.setText(mUserInfo.name);
+ setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().switchUser(mUserInfo.id);
+ WindowManagerGlobal.getWindowManagerService().lockNow();
+ mUserSelector.init();
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ }
+ });
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
new file mode 100644
index 0000000..ba99a55
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+
+public class KeyguardMultiUserSelectorView extends LinearLayout{
+ private KeyguardMultiUserAvatar mActiveUser;
+ private LinearLayout mInactiveUsers;
+
+ public KeyguardMultiUserSelectorView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ protected void onFinishInflate () {
+ init();
+ }
+
+ public void init() {
+ mActiveUser = (KeyguardMultiUserAvatar) findViewById(R.id.keyguard_active_user);
+ mInactiveUsers = (LinearLayout) findViewById(R.id.keyguard_inactive_users);
+
+ mInactiveUsers.removeAllViews();
+
+ UserInfo currentUser;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException re) {
+ currentUser = null;
+ }
+
+ UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers());
+ for (UserInfo user: users) {
+ if (user.id == currentUser.id) {
+ setActiveUser(user);
+ } else {
+ createAndAddInactiveUser(user);
+ }
+ }
+ }
+
+ private void setActiveUser(UserInfo user) {
+ mActiveUser.setup(user, this);
+ }
+
+ private void createAndAddInactiveUser(UserInfo user) {
+ KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
+ R.layout.keyguard_multi_user_avatar, mContext, this, user);
+ mInactiveUsers.addView(uv);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java
new file mode 100644
index 0000000..4f29825
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+public class KeyguardNavigationManager {
+
+ private TextView mMessageArea;
+ private KeyguardSecurityView mKeyguardSecurityView;
+ private View mClickArea;
+
+ public KeyguardNavigationManager(KeyguardSecurityView view) {
+ mKeyguardSecurityView = view;
+ mMessageArea = (TextView) ((View) view).findViewById(R.id.keyguard_message_area);
+ mMessageArea.setSelected(true); // Make marquee work
+
+ mClickArea = ((View) view).findViewById(R.id.keyguard_click_area);
+ mClickArea.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ mKeyguardSecurityView.getCallback().dismiss(false);
+ }
+ });
+ }
+
+ public void setMessage(CharSequence msg) {
+ mMessageArea.setText(msg);
+ }
+
+ public void setMessage(int resId) {
+ if (resId != 0) {
+ mMessageArea.setText(resId);
+ } else {
+ mMessageArea.setText("");
+ }
+ }
+
+ public void setMessage(int resId, Object... formatArgs) {
+ if (resId != 0) {
+ mMessageArea.setText(mMessageArea.getContext().getString(resId, formatArgs));
+ } else {
+ mMessageArea.setText("");
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
similarity index 68%
copy from policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index 203f9db..b2ce73e 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,96 +14,98 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
import java.util.List;
import android.app.admin.DevicePolicyManager;
-import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.PasswordEntryKeyboardView;
import android.os.CountDownTimer;
import android.os.SystemClock;
-import android.provider.Settings;
import android.security.KeyStore;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.text.method.TextKeyListener;
-import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.EditText;
import android.widget.LinearLayout;
-import android.widget.Space;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
-import com.android.internal.R;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
-
/**
* Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
- OnEditorActionListener {
- private static final String TAG = "PasswordUnlockScreen";
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardScreenCallback mCallback;
-
- private final boolean mIsAlpha;
-
- private final EditText mPasswordEntry;
- private final LockPatternUtils mLockPatternUtils;
- private final PasswordEntryKeyboardView mKeyboardView;
- private final PasswordEntryKeyboardHelper mKeyboardHelper;
-
- private final int mCreationOrientation;
- private final int mCreationHardKeyboardHidden;
-
- private final KeyguardStatusViewManager mStatusViewManager;
- private final boolean mUseSystemIME = true; // TODO: Make configurable
- private boolean mResuming; // used to prevent poking the wakelock during onResume()
+public class KeyguardPasswordView extends LinearLayout
+ implements KeyguardSecurityView, OnEditorActionListener {
+ private KeyguardSecurityCallback mCallback;
+ private EditText mPasswordEntry;
+ private LockPatternUtils mLockPatternUtils;
+ private PasswordEntryKeyboardView mKeyboardView;
+ private PasswordEntryKeyboardHelper mKeyboardHelper;
+ private boolean mIsAlpha;
+ private KeyguardNavigationManager mNavigationManager;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
- public PasswordUnlockScreen(Context context, Configuration configuration,
- LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor,
- KeyguardScreenCallback callback) {
+ public KeyguardPasswordView(Context context) {
super(context);
+ }
- mCreationHardKeyboardHidden = configuration.hardKeyboardHidden;
- mCreationOrientation = configuration.orientation;
- mUpdateMonitor = updateMonitor;
+ public KeyguardPasswordView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
mCallback = callback;
- mLockPatternUtils = lockPatternUtils;
+ }
- LayoutInflater layoutInflater = LayoutInflater.from(context);
- if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
- layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true);
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ public void reset() {
+ // start fresh
+ mPasswordEntry.setText("");
+ mPasswordEntry.requestFocus();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
} else {
- layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
+ mNavigationManager.setMessage(
+ mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions);
}
+ }
- mStatusViewManager = new KeyguardStatusViewManager(this, mUpdateMonitor, mLockPatternUtils,
- mCallback, true);
+ @Override
+ protected void onFinishInflate() {
+ mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one
- final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
+ mNavigationManager = new KeyguardNavigationManager(this);
+
+ final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
|| DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
|| DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
@@ -112,8 +114,9 @@
mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
mPasswordEntry.setOnEditorActionListener(this);
- mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false);
+ mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false);
mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+
boolean imeOrDeleteButtonVisible = false;
if (mIsAlpha) {
// We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
@@ -122,17 +125,16 @@
} else {
// Use lockscreen's numeric keyboard if the physical keyboard isn't showing
mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
- mKeyboardView.setVisibility(mCreationHardKeyboardHidden
+ mKeyboardView.setVisibility(getResources().getConfiguration().hardKeyboardHidden
== Configuration.HARDKEYBOARDHIDDEN_NO ? View.INVISIBLE : View.VISIBLE);
// The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
// not a separate view
- View pinDelete = findViewById(R.id.pinDel);
+ View pinDelete = findViewById(R.id.delete_button);
if (pinDelete != null) {
pinDelete.setVisibility(View.VISIBLE);
imeOrDeleteButtonVisible = true;
pinDelete.setOnClickListener(new OnClickListener() {
- @Override
public void onClick(View v) {
mKeyboardHelper.handleBackspace();
}
@@ -147,22 +149,19 @@
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
- //mStatusViewManager.setHelpMessage(R.string.keyguard_password_enter_password_code,
- //KeyguardStatusViewManager.LOCK_ICON);
} else {
mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_VARIATION_PASSWORD);
- //mStatusViewManager.setHelpMessage(R.string.keyguard_password_enter_pin_code,
- //KeyguardStatusViewManager.LOCK_ICON);
}
// Poke the wakelock any time the text is selected or modified
mPasswordEntry.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
- mCallback.pokeWakelock();
+ mCallback.userActivity(0); // TODO: customize timeout for text?
}
});
+
mPasswordEntry.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@@ -171,9 +170,7 @@
}
public void afterTextChanged(Editable s) {
- if (!mResuming) {
- mCallback.pokeWakelock();
- }
+ mCallback.userActivity(0);
}
});
@@ -186,7 +183,7 @@
imeOrDeleteButtonVisible = true;
switchImeButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
- mCallback.pokeWakelock(); // Leave the screen on a bit longer
+ mCallback.userActivity(0); // Leave the screen on a bit longer
imm.showInputMethodPicker();
}
});
@@ -257,60 +254,23 @@
return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
}
- /** {@inheritDoc} */
- public boolean needsInput() {
- return mUseSystemIME && mIsAlpha;
- }
-
- /** {@inheritDoc} */
- public void onPause() {
- mStatusViewManager.onPause();
- }
-
- /** {@inheritDoc} */
- public void onResume() {
- mResuming = true;
- // reset status
- mStatusViewManager.onResume();
-
- // start fresh
- mPasswordEntry.setText("");
- mPasswordEntry.requestFocus();
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- }
- mResuming = false;
- }
-
- /** {@inheritDoc} */
- public void cleanUp() {
- mUpdateMonitor.removeCallback(this);
- }
-
private void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
if (mLockPatternUtils.checkPassword(entry)) {
- mCallback.keyguardDone(true);
mCallback.reportSuccessfulUnlockAttempt();
- mStatusViewManager.setInstructionText(null);
KeyStore.getInstance().password(entry);
+ mCallback.dismiss(true);
} else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
mCallback.reportFailedUnlockAttempt();
- if (0 == (mUpdateMonitor.getFailedAttempts()
+ if (0 == (mCallback.getFailedAttempts()
% LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
- mStatusViewManager.setInstructionText(
- mContext.getString(R.string.lockscreen_password_wrong));
- } else if (entry.length() > 0) {
- mStatusViewManager.setInstructionText(
- mContext.getString(R.string.lockscreen_password_wrong));
+ mNavigationManager.setMessage(
+ mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin);
}
mPasswordEntry.setText("");
}
@@ -325,52 +285,25 @@
@Override
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) (millisUntilFinished / 1000);
- String instructions = getContext().getString(
- R.string.lockscreen_too_many_failed_attempts_countdown,
- secondsRemaining);
- mStatusViewManager.setInstructionText(instructions);
+ mNavigationManager.setMessage(
+ R.string.kg_too_many_failed_attempts_countdown, secondsRemaining);
}
@Override
public void onFinish() {
mPasswordEntry.setEnabled(true);
mKeyboardView.setEnabled(true);
- mStatusViewManager.resetStatusInfo();
}
}.start();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- mCallback.pokeWakelock();
+ mCallback.userActivity(0);
return false;
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Configuration config = getResources().getConfiguration();
- if (config.orientation != mCreationOrientation
- || config.hardKeyboardHidden != mCreationHardKeyboardHidden) {
- mCallback.recreateMe(config);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (newConfig.orientation != mCreationOrientation
- || newConfig.hardKeyboardHidden != mCreationHardKeyboardHidden) {
- mCallback.recreateMe(newConfig);
- }
- }
-
- public void onKeyboardChange(boolean isKeyboardOpen) {
- // Don't show the soft keyboard when the real keyboard is open
- mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE);
- }
-
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
@@ -380,4 +313,26 @@
}
return false;
}
+
+ @Override
+ public boolean needsInput() {
+ return mIsAlpha;
+ }
+
+ @Override
+ public void onPause() {
+
+ }
+
+ @Override
+ public void onResume() {
+ reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
}
+
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
new file mode 100644
index 0000000..3a32b5b
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.security.KeyStore;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.R;
+
+import java.io.IOException;
+import java.util.List;
+
+public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView {
+
+ private static final String TAG = "SecurityPatternView";
+ private static final boolean DEBUG = false;
+
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
+ private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
+
+ // how long we stay awake after the user hits the first dot.
+ private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
+
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+ private int mFailedPatternAttemptsSinceLastTimeout = 0;
+ private int mTotalFailedPatternAttempts = 0;
+ private CountDownTimer mCountdownTimer = null;
+ private LockPatternUtils mLockPatternUtils;
+ private LockPatternView mLockPatternView;
+ private Button mForgotPatternButton;
+ private KeyguardSecurityCallback mCallback;
+ private boolean mEnableFallback;
+ private KeyguardNavigationManager mNavigationManager;
+
+ /**
+ * Keeps track of the last time we poked the wake lock during dispatching of the touch event.
+ * Initialized to something guaranteed to make us poke the wakelock when the user starts
+ * drawing the pattern.
+ * @see #dispatchTouchEvent(android.view.MotionEvent)
+ */
+ private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
+
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ enum FooterMode {
+ Normal,
+ ForgotLockPattern,
+ VerifyUnlocked
+ }
+
+ public KeyguardPatternView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPatternView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mNavigationManager = new KeyguardNavigationManager(this);
+ mLockPatternUtils = mLockPatternUtils == null
+ ? new LockPatternUtils(mContext) : mLockPatternUtils;
+
+ mLockPatternView = (LockPatternView) findViewById(R.id.lockPatternView);
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setFocusable(false);
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+
+ // stealth mode will be the same for the life of this screen
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
+
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ mForgotPatternButton = (Button) findViewById(R.id.forgot_password_button);
+ mForgotPatternButton.setText(R.string.kg_forgot_pattern_button_text);
+ mForgotPatternButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.showBackupSecurity();
+ }
+ });
+
+ setFocusableInTouchMode(true);
+
+ maybeEnableFallback(mContext);
+ }
+
+ private void updateFooter(FooterMode mode) {
+ switch (mode) {
+ case Normal:
+ if (DEBUG) Log.d(TAG, "mode normal");
+ mForgotPatternButton.setVisibility(View.GONE);
+ break;
+ case ForgotLockPattern:
+ if (DEBUG) Log.d(TAG, "mode ForgotLockPattern");
+ mForgotPatternButton.setVisibility(View.VISIBLE);
+ break;
+ case VerifyUnlocked:
+ if (DEBUG) Log.d(TAG, "mode VerifyUnlocked");
+ mForgotPatternButton.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ final boolean result = super.dispatchTouchEvent(ev);
+ // as long as the user is entering a pattern (i.e sending a touch event that was handled
+ // by this screen), keep poking the wake lock so that the screen will stay on.
+ final long elapsed = SystemClock.elapsedRealtime() - mLastPokeTime;
+ if (result && (elapsed > (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
+ mLastPokeTime = SystemClock.elapsedRealtime();
+ }
+ return result;
+ }
+
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_pattern_instructions);
+ }
+
+ // the footer depends on how many total attempts the user has failed
+ if (mCallback.isVerifyUnlockOnly()) {
+ updateFooter(FooterMode.VerifyUnlocked);
+ } else if (mEnableFallback &&
+ (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ updateFooter(FooterMode.ForgotLockPattern);
+ } else {
+ updateFooter(FooterMode.Normal);
+ }
+
+ }
+
+ /** TODO: hook this up */
+ public void cleanUp() {
+ if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
+ mLockPatternUtils = null;
+ mLockPatternView.setOnPatternListener(null);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (hasWindowFocus) {
+ // when timeout dialog closes we want to update our state
+ reset();
+ }
+ }
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ }
+
+ public void onPatternCleared() {
+ }
+
+ public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
+ // To guard against accidental poking of the wakelock, look for
+ // the user actually trying to draw a pattern of some minimal length.
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+ } else {
+ // Give just a little extra time if they hit one of the first few dots
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
+ }
+ }
+
+ public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+ if (mLockPatternUtils.checkPattern(pattern)) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ mCallback.dismiss(true); // keyguardDone(true)
+ KeyStore.getInstance().password(LockPatternUtils.patternToString(pattern));
+ mTotalFailedPatternAttempts = 0;
+ } else {
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+ }
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mTotalFailedPatternAttempts++;
+ mFailedPatternAttemptsSinceLastTimeout++;
+ mCallback.reportFailedUnlockAttempt();
+ }
+ if (mFailedPatternAttemptsSinceLastTimeout
+ >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_wrong_pattern);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ private void maybeEnableFallback(Context context) {
+ // Ask the account manager if we have an account that can be used as a
+ // fallback in case the user forgets his pattern.
+ AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
+ accountAnalyzer.start();
+ }
+
+ private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
+ private final AccountManager mAccountManager;
+ private final Account[] mAccounts;
+ private int mAccountIndex;
+
+ private AccountAnalyzer(AccountManager accountManager) {
+ mAccountManager = accountManager;
+ mAccounts = accountManager.getAccountsByType("com.google");
+ }
+
+ private void next() {
+ // if we are ready to enable the fallback or if we depleted the list of accounts
+ // then finish and get out
+ if (mAccountIndex >= mAccounts.length) {
+ mEnableFallback = true;
+ return;
+ }
+
+ // lookup the confirmCredentials intent for the current account
+ mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
+ }
+
+ public void start() {
+ mEnableFallback = false;
+ mAccountIndex = 0;
+ next();
+ }
+
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle result = future.getResult();
+ if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
+ mEnableFallback = true;
+ }
+ } catch (OperationCanceledException e) {
+ // just skip the account if we are unable to query it
+ } catch (IOException e) {
+ // just skip the account if we are unable to query it
+ } catch (AuthenticatorException e) {
+ // just skip the account if we are unable to query it
+ } finally {
+ mAccountIndex++;
+ next();
+ }
+ }
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ updateFooter(FooterMode.ForgotLockPattern);
+
+ mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) (millisUntilFinished / 1000);
+ mNavigationManager.setMessage(
+ R.string.kg_too_many_failed_attempts_countdown, secondsRemaining);
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ mNavigationManager.setMessage(R.string.kg_pattern_instructions);
+ // TODO mUnlockIcon.setVisibility(View.VISIBLE);
+ mFailedPatternAttemptsSinceLastTimeout = 0;
+ if (mEnableFallback) {
+ updateFooter(FooterMode.ForgotLockPattern);
+ } else {
+ updateFooter(FooterMode.Normal);
+ }
+ }
+
+ }.start();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+}
+
+
+
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java
new file mode 100644
index 0000000..3b8df5d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+public interface KeyguardSecurityCallback {
+
+ /**
+ * Dismiss the given security screen.
+ * @param securityVerified true if the user correctly entered credentials for the given screen.
+ */
+ void dismiss(boolean securityVerified);
+
+ /**
+ * Manually report user activity to keep the device awake. If timeout is 0,
+ * uses user-defined timeout.
+ * @param timeout
+ */
+ void userActivity(long timeout);
+
+ /**
+ * Checks if keyguard is in "verify credentials" mode.
+ * @return true if user has been asked to verify security.
+ */
+ boolean isVerifyUnlockOnly();
+
+ /**
+ * Call when user correctly enters their credentials
+ */
+ void reportSuccessfulUnlockAttempt();
+
+ /**
+ * Call when the user incorrectly enters their credentials
+ */
+ void reportFailedUnlockAttempt();
+
+ /**
+ * Gets the number of attempts thus far as reported by {@link #reportFailedUnlockAttempt()}
+ * @return number of failed attempts
+ */
+ int getFailedAttempts();
+
+ /**
+ * Shows the backup security for the current method. If none available, this call is a no-op.
+ */
+ void showBackupSecurity();
+
+ /**
+ * Sets a runnable to launch after the user successfully enters their credentials.
+ * @param runnable
+ */
+ void setOnDismissRunnable(Runnable runnable);
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
new file mode 100644
index 0000000..75c4a7c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
+public class KeyguardSecurityModel {
+ /**
+ * The different types of security available for {@link Mode#UnlockScreen}.
+ * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
+ */
+ enum SecurityMode {
+ None, // No security enabled
+ Pattern, // Unlock by drawing a pattern.
+ Password, // Unlock by entering a password or PIN
+ Biometric, // Unlock with a biometric key (e.g. finger print or face unlock)
+ Account, // Unlock by entering an account's login and password.
+ SimPin, // Unlock by entering a sim pin.
+ SimPuk // Unlock by entering a sim puk
+ }
+
+ private Context mContext;
+ private LockPatternUtils mLockPatternUtils;
+
+ KeyguardSecurityModel(Context context) {
+ mContext = context;
+ mLockPatternUtils = new LockPatternUtils(context);
+ }
+
+ void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ /**
+ * This returns false if there is any condition that indicates that the biometric unlock should
+ * not be used before the next time the unlock screen is recreated. In other words, if this
+ * returns false there is no need to even construct the biometric unlock.
+ */
+ private boolean isBiometricUnlockEnabled() {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final boolean backupIsTimedOut =
+ monitor.getFailedUnlockAttempts() >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+ return mLockPatternUtils.usingBiometricWeak()
+ && mLockPatternUtils.isBiometricWeakInstalled()
+ && !monitor.getMaxBiometricUnlockAttemptsReached()
+ && !backupIsTimedOut;
+ }
+
+ SecurityMode getSecurityMode() {
+ KeyguardUpdateMonitor mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final IccCardConstants.State simState = mUpdateMonitor.getSimState();
+ SecurityMode mode = SecurityMode.None;
+ if (simState == IccCardConstants.State.PIN_REQUIRED) {
+ mode = SecurityMode.SimPin;
+ } else if (simState == IccCardConstants.State.PUK_REQUIRED
+ && mLockPatternUtils.isPukUnlockScreenEnable()) {
+ mode = SecurityMode.SimPuk;
+ } else {
+ final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
+ switch (security) {
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ mode = mLockPatternUtils.isLockPasswordEnabled() ?
+ SecurityMode.Password : SecurityMode.None;
+ break;
+
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+ if (mLockPatternUtils.isLockPatternEnabled()) {
+ mode = mLockPatternUtils.isPermanentlyLocked() ?
+ SecurityMode.Account : SecurityMode.Pattern;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown unlock mode:" + mode);
+ }
+ }
+ return mode;
+ }
+
+ /**
+ * Some unlock methods can have an alternate, such as biometric unlocks (e.g. face unlock).
+ * This function decides if an alternate unlock is available and returns it. Otherwise,
+ * returns @param mode.
+ *
+ * @param mode the mode we want the alternate for
+ * @return alternate or the given mode
+ */
+ SecurityMode getAlternateFor(SecurityMode mode) {
+ if (isBiometricUnlockEnabled()
+ && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) {
+ return SecurityMode.Biometric;
+ }
+ return mode; // no alternate, return what was given
+ }
+
+ /**
+ * Some unlock methods can have a backup which gives the user another way to get into
+ * the device. This is currently only supported for Biometric and Pattern unlock.
+ *
+ * @param mode the mode we want the backup for
+ * @return backup method or given mode
+ */
+ SecurityMode getBackupFor(SecurityMode mode) {
+ switch(mode) {
+ case Biometric:
+ return getSecurityMode();
+ case Pattern:
+ return SecurityMode.Account;
+ }
+ return mode; // no backup, return what was given
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
new file mode 100644
index 0000000..d80c1db
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import com.android.internal.widget.LockPatternUtils;
+
+public interface KeyguardSecurityView {
+ /**
+ * Interface back to keyguard to tell it when security
+ * @param callback
+ */
+ void setKeyguardCallback(KeyguardSecurityCallback callback);
+
+ /**
+ * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
+ * @param utils
+ */
+ void setLockPatternUtils(LockPatternUtils utils);
+
+ /**
+ * Reset the view and prepare to take input. This should do things like clearing the
+ * password or pattern and clear error messages.
+ */
+ void reset();
+
+ /**
+ * Emulate activity life cycle within the view. When called, the view should clean up
+ * and prepare to be removed.
+ */
+ void onPause();
+
+ /**
+ * Emulate activity life cycle within this view. When called, the view should prepare itself
+ * to be shown.
+ */
+ void onResume();
+
+ /**
+ * Inquire whether this view requires IME (keyboard) interaction.
+ *
+ * @return true if IME interaction is required.
+ */
+ boolean needsInput();
+
+ /**
+ * Get {@link KeyguardSecurityCallback} for the given object
+ * @return KeyguardSecurityCallback
+ */
+ KeyguardSecurityCallback getCallback();
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
new file mode 100644
index 0000000..95772af
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.animation.ObjectAnimator;
+import android.app.ActivityManagerNative;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.multiwaveview.GlowPadView;
+import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
+import com.android.internal.R;
+
+public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final String TAG = "SecuritySelectorView";
+ private static final String ASSIST_ICON_METADATA_NAME =
+ "com.android.systemui.action_assist_icon";
+ private static final int EMERGENCY_CALL_TIMEOUT = 10000; // screen timeout after starting e.d.
+ static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+ private KeyguardSecurityCallback mCallback;
+ private GlowPadView mGlowPadView;
+ private Button mEmergencyCallButton;
+ private ObjectAnimator mAnim;
+ private boolean mCameraDisabled;
+ private boolean mSearchDisabled;
+ private LockPatternUtils mLockPatternUtils;
+
+ OnTriggerListener mOnTriggerListener = new OnTriggerListener() {
+
+ public void onTrigger(View v, int target) {
+ final int resId = mGlowPadView.getResourceIdForTarget(target);
+ switch (resId) {
+ case com.android.internal.R.drawable.ic_action_assist_generic:
+ Intent assistIntent =
+ ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
+ if (assistIntent != null) {
+ launchActivity(assistIntent, false);
+ } else {
+ Log.w(TAG, "Failed to get intent for assist activity");
+ }
+ mCallback.userActivity(0);
+ break;
+
+ case com.android.internal.R.drawable.ic_lockscreen_camera:
+ launchCamera();
+ mCallback.userActivity(0);
+ break;
+
+ case com.android.internal.R.drawable.ic_lockscreen_unlock_phantom:
+ case com.android.internal.R.drawable.ic_lockscreen_unlock:
+ mCallback.dismiss(false);
+ break;
+ }
+ }
+
+ public void onReleased(View v, int handle) {
+ doTransition(mEmergencyCallButton, 1.0f);
+ }
+
+ public void onGrabbed(View v, int handle) {
+ doTransition(mEmergencyCallButton, 0.0f);
+ }
+
+ public void onGrabbedStateChange(View v, int handle) {
+
+ }
+
+ public void onFinishFinalAnimation() {
+
+ }
+
+ };
+
+ private void updateEmergencyCallButton(State simState, int phoneState) {
+ if (mEmergencyCallButton != null) {
+ boolean en = mLockPatternUtils.isEmergencyCallCapable()
+ || (phoneState == TelephonyManager.CALL_STATE_OFFHOOK); // voice call in progress
+ if (en && KeyguardUpdateMonitor.isSimLocked(simState)) {
+ // Some countries can't handle emergency calls while SIM is locked.
+ en = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked();
+ }
+ mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton, phoneState, en);
+ }
+ }
+
+ KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onDevicePolicyManagerStateChanged() {
+ updateTargets();
+ }
+
+ @Override
+ public void onSimStateChanged(State simState) {
+ int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState();
+ updateEmergencyCallButton(simState, phoneState);
+ updateTargets();
+ }
+
+ void onPhoneStateChanged(int phoneState) {
+ State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState();
+ updateEmergencyCallButton(simState, phoneState);
+ };
+ };
+
+ public KeyguardSelectorView(Context context) {
+ this(context, null);
+ }
+
+ protected void launchCamera() {
+ if (mLockPatternUtils.isSecure()) {
+ // Launch the secure version of the camera
+ launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE), true);
+ } else {
+ // Launch the normal camera
+ launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false);
+ }
+ }
+
+ public KeyguardSelectorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
+ mGlowPadView.setOnTriggerListener(mOnTriggerListener);
+ mEmergencyCallButton = (Button) findViewById(R.id.emergency_call_button);
+ mEmergencyCallButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ takeEmergencyCallAction();
+ }
+ });
+ updateTargets();
+ }
+
+ /**
+ * Shows the emergency dialer or returns the user to the existing call.
+ */
+ public void takeEmergencyCallAction() {
+ mCallback.userActivity(EMERGENCY_CALL_TIMEOUT);
+ if (TelephonyManager.getDefault().getCallState()
+ == TelephonyManager.CALL_STATE_OFFHOOK) {
+ mLockPatternUtils.resumeCall();
+ } else {
+ Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ getContext().startActivity(intent);
+ }
+ }
+
+ public boolean isTargetPresent(int resId) {
+ return mGlowPadView.getTargetPosition(resId) != -1;
+ }
+
+ private void updateTargets() {
+ boolean disabledByAdmin = mLockPatternUtils.getDevicePolicyManager()
+ .getCameraDisabled(null);
+ final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext());
+ boolean disabledBySimState = monitor.isSimLocked();
+ boolean cameraTargetPresent =
+ isTargetPresent(com.android.internal.R.drawable.ic_lockscreen_camera);
+ boolean searchTargetPresent =
+ isTargetPresent(com.android.internal.R.drawable.ic_action_assist_generic);
+
+ if (disabledByAdmin) {
+ Log.v(TAG, "Camera disabled by Device Policy");
+ } else if (disabledBySimState) {
+ Log.v(TAG, "Camera disabled by Sim State");
+ }
+ boolean searchActionAvailable =
+ ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
+ mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent;
+ mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
+ updateResources();
+ }
+
+ public void updateResources() {
+ // Update the search icon with drawable from the search .apk
+ if (!mSearchDisabled) {
+ Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT);
+ if (intent != null) {
+ // XXX Hack. We need to substitute the icon here but haven't formalized
+ // the public API. The "_google" metadata will be going away, so
+ // DON'T USE IT!
+ ComponentName component = intent.getComponent();
+ boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
+ ASSIST_ICON_METADATA_NAME + "_google",
+ com.android.internal.R.drawable.ic_action_assist_generic);
+
+ if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
+ ASSIST_ICON_METADATA_NAME,
+ com.android.internal.R.drawable.ic_action_assist_generic)) {
+ Slog.w(TAG, "Couldn't grab icon from package " + component);
+ }
+ }
+ }
+
+ mGlowPadView.setEnableTarget(com.android.internal.R.drawable
+ .ic_lockscreen_camera, !mCameraDisabled);
+ mGlowPadView.setEnableTarget(com.android.internal.R.drawable
+ .ic_action_assist_generic, !mSearchDisabled);
+ }
+
+ void doTransition(Object v, float to) {
+ if (mAnim != null) {
+ mAnim.cancel();
+ }
+ mAnim = ObjectAnimator.ofFloat(mEmergencyCallButton, "alpha", to);
+ mAnim.start();
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ /**
+ * Launches the said intent for the current foreground user.
+ * @param intent
+ * @param showsWhileLocked true if the activity can be run on top of keyguard.
+ * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
+ */
+ private void launchActivity(final Intent intent, boolean showsWhileLocked) {
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ boolean isSecure = mLockPatternUtils.isSecure();
+ if (!isSecure || showsWhileLocked) {
+ if (!isSecure) try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ Log.w(TAG, "can't dismiss keyguard on launch");
+ }
+ try {
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found for intent + " + intent.getAction());
+ }
+ } else {
+ // Create a runnable to start the activity and ask the user to enter their
+ // credentials.
+ mCallback.setOnDismissRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ mCallback.dismiss(false);
+ }
+ }
+
+ @Override
+ public void reset() {
+ mGlowPadView.reset(false);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mInfoCallback);
+ }
+
+ @Override
+ public void onResume() {
+ KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mInfoCallback);
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
new file mode 100644
index 0000000..bc55008
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.PasswordEntryKeyboardHelper;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+import com.android.internal.R;
+
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * Displays a dialer like interface to unlock the SIM PIN.
+ */
+public class KeyguardSimPinView extends LinearLayout
+ implements KeyguardSecurityView, OnEditorActionListener {
+
+ private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+ private EditText mPinEntry;
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private KeyguardSecurityCallback mCallback;
+ private PasswordEntryKeyboardView mKeyboardView;
+ private PasswordEntryKeyboardHelper mKeyboardHelper;
+ private LockPatternUtils mLockPatternUtils;
+ private KeyguardNavigationManager mNavigationManager;
+
+ private volatile boolean mSimCheckInProgress;
+
+ public KeyguardSimPinView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSimPinView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mNavigationManager = new KeyguardNavigationManager(this);
+
+ mPinEntry = (EditText) findViewById(R.id.sim_pin_entry);
+ mPinEntry.setOnEditorActionListener(this);
+
+ mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
+ mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false);
+ mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+ mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ final View deleteButton = findViewById(R.id.delete_button);
+ if (deleteButton != null) {
+ deleteButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mKeyboardHelper.handleBackspace();
+ }
+ });
+ }
+ reset();
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ return mPinEntry.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ public void reset() {
+ // start fresh
+ mNavigationManager.setMessage(R.string.kg_sim_pin_instructions);
+
+ // make sure that the number of entered digits is consistent when we
+ // erase the SIM unlock code, including orientation changes.
+ mPinEntry.setText("");
+ mPinEntry.requestFocus();
+ }
+
+ /** {@inheritDoc} */
+ public void cleanUp() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+
+ protected CheckSimPin(String pin) {
+ mPin = pin;
+ }
+
+ abstract void onSimCheckResponse(boolean success);
+
+ @Override
+ public void run() {
+ try {
+ final boolean result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPin(mPin);
+ post(new Runnable() {
+ public void run() {
+ onSimCheckResponse(result);
+ }
+ });
+ } catch (RemoteException e) {
+ post(new Runnable() {
+ public void run() {
+ onSimCheckResponse(false);
+ }
+ });
+ }
+ }
+ }
+
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ mCallback.userActivity(DIGIT_PRESS_WAKE_MILLIS);
+ if (event.getAction() == MotionEvent.ACTION_DOWN && (
+ actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT)) {
+ checkPin();
+ return true;
+ }
+ return false;
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(
+ mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mContext instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private void checkPin() {
+ if (mPinEntry.getText().length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_pin_hint);
+ mPinEntry.setText("");
+ mCallback.userActivity(0);
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (!mSimCheckInProgress) {
+ mSimCheckInProgress = true; // there should be only one
+ new CheckSimPin(mPinEntry.getText().toString()) {
+ void onSimCheckResponse(final boolean success) {
+ post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ // before closing the keyguard, report back that the sim is unlocked
+ // so it knows right away.
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
+ mCallback.dismiss(true);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_password_wrong_pin_code);
+ mPinEntry.setText("");
+ }
+ mCallback.userActivity(0);
+ mSimCheckInProgress = false;
+ }
+ });
+ }
+ }.start();
+ }
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ public boolean needsInput() {
+ return false; // This view provides its own keypad
+ }
+
+ public void onPause() {
+
+ }
+
+ public void onResume() {
+ reset();
+ }
+
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
new file mode 100644
index 0000000..e04bff9
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.Editable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.PasswordEntryKeyboardHelper;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+import com.android.internal.R;
+
+public class KeyguardSimPukView extends LinearLayout implements View.OnClickListener,
+ View.OnFocusChangeListener, KeyguardSecurityView, OnEditorActionListener {
+
+ private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+ private TextView mPukText;
+ private TextView mPinText;
+ private TextView mFocusedEntry;
+
+ private View mDelPukButton;
+ private View mDelPinButton;
+
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private KeyguardSecurityCallback mCallback;
+
+ private KeyguardNavigationManager mNavigationManager;
+
+ private PasswordEntryKeyboardView mKeyboardView;
+
+ private PasswordEntryKeyboardHelper mKeyboardHelper;
+
+ private LockPatternUtils mLockPatternUtils;
+
+ private volatile boolean mCheckInProgress;
+
+ public KeyguardSimPukView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardSimPukView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ mLockPatternUtils = new LockPatternUtils(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mNavigationManager = new KeyguardNavigationManager(this);
+
+ mPukText = (TextView) findViewById(R.id.sim_puk_entry);
+ mPukText.setOnEditorActionListener(this);
+ mPinText = (TextView) findViewById(R.id.sim_pin_entry);
+ mPinText.setOnEditorActionListener(this);
+ mDelPukButton = findViewById(R.id.puk_delete_button);
+ mDelPukButton.setOnClickListener(this);
+ mDelPinButton = findViewById(R.id.pin_delete_button);
+ mDelPinButton.setOnClickListener(this);
+
+ mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
+ mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false);
+ mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+ mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ mNavigationManager.setMessage(R.string.kg_sim_puk_recovery_hint);
+
+ mPinText.setFocusableInTouchMode(true);
+ mPinText.setOnFocusChangeListener(this);
+ mPukText.setFocusableInTouchMode(true);
+ mPukText.setOnFocusChangeListener(this);
+
+ setFocusableInTouchMode(true);
+
+ reset();
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ return mPukText.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ public boolean needsInput() {
+ return false; // This view provides its own keypad
+ }
+
+ public void onPause() {
+
+ }
+
+ public void onResume() {
+ reset();
+ }
+
+ /** {@inheritDoc} */
+ public void cleanUp() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+
+ protected CheckSimPuk(String puk, String pin) {
+ mPuk = puk;
+ mPin = pin;
+ }
+
+ abstract void onSimLockChangedResponse(boolean success);
+
+ @Override
+ public void run() {
+ try {
+ final boolean result = ITelephony.Stub.asInterface(ServiceManager
+ .checkService("phone")).supplyPuk(mPuk, mPin);
+
+ post(new Runnable() {
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ } catch (RemoteException e) {
+ post(new Runnable() {
+ public void run() {
+ onSimLockChangedResponse(false);
+ }
+ });
+ }
+ }
+ }
+
+ public void onClick(View v) {
+ if (v == mDelPukButton) {
+ if (mFocusedEntry != mPukText)
+ mPukText.requestFocus();
+ final Editable digits = mPukText.getEditableText();
+ final int len = digits.length();
+ if (len > 0) {
+ digits.delete(len-1, len);
+ }
+ } else if (v == mDelPinButton) {
+ if (mFocusedEntry != mPinText)
+ mPinText.requestFocus();
+ final Editable digits = mPinText.getEditableText();
+ final int len = digits.length();
+ if (len > 0) {
+ digits.delete(len-1, len);
+ }
+ }
+ mCallback.userActivity(DIGIT_PRESS_WAKE_MILLIS);
+ }
+
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus)
+ mFocusedEntry = (TextView) view;
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(mContext.getString(
+ R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mContext instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private void checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPukText.getText().length() < 8) {
+ // otherwise, display a message to the user, and don't submit.
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_puk_hint);
+ mPukText.setText("");
+ return;
+ }
+
+ // make sure the PIN is between 4 and 8 digits
+ if (mPinText.getText().length() < 4
+ || mPinText.getText().length() > 8) {
+ // otherwise, display a message to the user, and don't submit.
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_pin_hint);
+ mPinText.setText("");
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (!mCheckInProgress) {
+ mCheckInProgress = true;
+ new CheckSimPuk(mPukText.getText().toString(),
+ mPinText.getText().toString()) {
+ void onSimLockChangedResponse(final boolean success) {
+ mPinText.post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ mCallback.dismiss(true);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_invalid_puk);
+ mPukText.setText("");
+ mPinText.setText("");
+ }
+ mCheckInProgress = false;
+ }
+ });
+ }
+ }.start();
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ mCallback.userActivity(DIGIT_PRESS_WAKE_MILLIS);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
+ if (view == mPukText && mPukText.getText().length() < 8) {
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_puk_hint);
+ mPukText.setText("");
+ mPukText.requestFocus();
+ return true;
+ } else if (view == mPinText) {
+ if (mPinText.getText().length() < 4 || mPinText.getText().length() > 8) {
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_pin_hint);
+ mPinText.setText("");
+ mPinText.requestFocus();
+ } else {
+ checkPuk();
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ public void reset() {
+ mNavigationManager.setMessage(R.string.kg_sim_puk_recovery_hint);
+ mPinText.setText("");
+ mPukText.setText("");
+ mPukText.requestFocus();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
new file mode 100644
index 0000000..5704425
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.GridLayout;
+
+public class KeyguardStatusView extends GridLayout {
+ @SuppressWarnings("unused")
+ private KeyguardStatusViewManager mStatusViewManager;
+
+ public KeyguardStatusView(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardStatusView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ // StatusView manages all of the widgets in this view.
+ mStatusViewManager = new KeyguardStatusViewManager(this);
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
similarity index 71%
copy from policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index bb07a1d..e325f94 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import com.android.internal.R;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.DigitalClock;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.TransportControlView;
-import com.android.internal.policy.impl.KeyguardUpdateMonitor.BatteryStatus;
-import java.util.ArrayList;
import java.util.Date;
import libcore.util.MutableInt;
@@ -35,15 +32,12 @@
import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.TextView;
/***
- * Manages a number of views inside of LockScreen layouts. See below for a list of widgets
- *
+ * Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-class KeyguardStatusViewManager implements OnClickListener {
+class KeyguardStatusViewManager {
private static final boolean DEBUG = false;
private static final String TAG = "KeyguardStatusView";
@@ -51,7 +45,6 @@
public static final int ALARM_ICON = R.drawable.ic_lock_idle_alarm;
public static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
- private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets
private static final int INSTRUCTION_TEXT = 10;
private static final int CARRIER_TEXT = 11;
@@ -62,7 +55,6 @@
private StatusMode mStatus;
private String mDateFormatString;
- private TransientTextManager mTransientTextManager;
// Views that this class controls.
// NOTE: These may be null in some LockScreen screens and should protect from NPE
@@ -71,7 +63,6 @@
private TextView mStatus1View;
private TextView mOwnerInfoView;
private TextView mAlarmStatusView;
- private TransportControlView mTransportView;
// Top-level container view for above views
private View mContainer;
@@ -90,8 +81,6 @@
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
- private Button mEmergencyCallButton;
- private boolean mEmergencyButtonEnabledBecauseSimLocked;
// Shadowed text values
private CharSequence mCarrierText;
@@ -100,105 +89,29 @@
private String mInstructionText;
private CharSequence mOwnerInfoText;
private boolean mShowingStatus;
- private KeyguardScreenCallback mCallback;
- private final boolean mEmergencyCallButtonEnabledInScreen;
private CharSequence mPlmn;
private CharSequence mSpn;
- protected int mPhoneState;
private DigitalClock mDigitalClock;
protected boolean mBatteryCharged;
protected boolean mBatteryIsLow;
- private class TransientTextManager {
- private TextView mTextView;
- private class Data {
- final int icon;
- final CharSequence text;
- Data(CharSequence t, int i) {
- text = t;
- icon = i;
- }
- };
- private ArrayList<Data> mMessages = new ArrayList<Data>(5);
-
- TransientTextManager(TextView textView) {
- mTextView = textView;
- }
-
- /* Show given message with icon for up to duration ms. Newer messages override older ones.
- * The most recent message with the longest duration is shown as messages expire until
- * nothing is left, in which case the text/icon is defined by a call to
- * getAltTextMessage() */
- void post(final CharSequence message, final int icon, long duration) {
- if (mTextView == null) {
- return;
- }
- mTextView.setText(message);
- mTextView.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
- final Data data = new Data(message, icon);
- mContainer.postDelayed(new Runnable() {
- public void run() {
- mMessages.remove(data);
- int last = mMessages.size() - 1;
- final CharSequence lastText;
- final int lastIcon;
- if (last > 0) {
- final Data oldData = mMessages.get(last);
- lastText = oldData.text;
- lastIcon = oldData.icon;
- } else {
- final MutableInt tmpIcon = new MutableInt(0);
- lastText = getAltTextMessage(tmpIcon);
- lastIcon = tmpIcon.value;
- }
- mTextView.setText(lastText);
- mTextView.setCompoundDrawablesWithIntrinsicBounds(lastIcon, 0, 0, 0);
- }
- }, duration);
- }
- };
-
/**
- *
* @param view the containing view of all widgets
- * @param updateMonitor the update monitor to use
- * @param lockPatternUtils lock pattern util object
- * @param callback used to invoke emergency dialer
- * @param emergencyButtonEnabledInScreen whether emergency button is enabled by default
*/
- public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,
- LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,
- boolean emergencyButtonEnabledInScreen) {
+ public KeyguardStatusViewManager(View view) {
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
mDateFormatString = getContext().getString(R.string.abbrev_wday_month_day_no_year);
- mLockPatternUtils = lockPatternUtils;
- mUpdateMonitor = updateMonitor;
- mCallback = callback;
+ mLockPatternUtils = new LockPatternUtils(view.getContext());
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(view.getContext());
mCarrierView = (TextView) findViewById(R.id.carrier);
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
- mTransportView = (TransportControlView) findViewById(R.id.transport);
- mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
- mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mDigitalClock = (DigitalClock) findViewById(R.id.time);
- // Hide transport control view until we know we need to show it.
- if (mTransportView != null) {
- mTransportView.setVisibility(View.GONE);
- }
-
- if (mEmergencyCallButton != null) {
- mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
- mEmergencyCallButton.setOnClickListener(this);
- mEmergencyCallButton.setFocusable(false); // touch only!
- }
-
- mTransientTextManager = new TransientTextManager(mCarrierView);
-
// Registering this callback immediately updates the battery state, among other things.
mUpdateMonitor.registerCallback(mInfoCallback);
@@ -216,10 +129,6 @@
}
}
- private boolean inWidgetMode() {
- return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE;
- }
-
void setInstructionText(String string) {
mInstructionText = string;
update(INSTRUCTION_TEXT, string);
@@ -264,25 +173,7 @@
}
private void update(int what, CharSequence string) {
- if (inWidgetMode()) {
- if (DEBUG) Log.v(TAG, "inWidgetMode() is true");
- // Use Transient text for messages shown while widget is shown.
- switch (what) {
- case INSTRUCTION_TEXT:
- case CARRIER_HELP_TEXT:
- case HELP_MESSAGE_TEXT:
- case BATTERY_INFO:
- mTransientTextManager.post(string, 0, INSTRUCTION_RESET_DELAY);
- break;
-
- case OWNER_INFO:
- case CARRIER_TEXT:
- default:
- if (DEBUG) Log.w(TAG, "Not showing message id " + what + ", str=" + string);
- }
- } else {
- updateStatusLines(mShowingStatus);
- }
+ updateStatusLines(mShowingStatus);
}
public void onPause() {
@@ -301,12 +192,6 @@
mUpdateMonitor.registerCallback(mInfoCallback);
resetStatusInfo();
- // Issue the biometric unlock failure message in a centralized place
- // TODO: we either need to make the Face Unlock multiple failures string a more general
- // 'biometric unlock' or have each biometric unlock handle this on their own.
- if (mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
- setInstructionText(getContext().getString(R.string.faceunlock_multiple_failures));
- }
}
void resetStatusInfo() {
@@ -363,9 +248,7 @@
}
private void updateCarrierText() {
- if (!inWidgetMode() && mCarrierView != null) {
- mCarrierView.setText(mCarrierText);
- }
+ mCarrierView.setText(mCarrierText);
}
private CharSequence getAltTextMessage(MutableInt icon) {
@@ -408,8 +291,7 @@
string = getContext().getString(R.string.lockscreen_low_battery);
icon.value = BATTERY_LOW_ICON;
}
- } else if (!inWidgetMode() && mOwnerInfoView == null && mOwnerInfoText != null) {
- // OwnerInfo shows in status if we don't have a dedicated widget
+ } else if (mOwnerInfoView == null && mOwnerInfoText != null) {
string = mOwnerInfoText;
}
return string;
@@ -472,7 +354,6 @@
CharSequence carrierText = null;
int carrierHelpTextId = 0;
- mEmergencyButtonEnabledBecauseSimLocked = false;
mStatus = getStatusForIccState(simState);
mSimState = simState;
switch (mStatus) {
@@ -502,7 +383,6 @@
carrierText = getContext().getText(
R.string.lockscreen_permanent_disabled_sim_message_short);
carrierHelpTextId = R.string.lockscreen_permanent_disabled_sim_instructions;
- mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimMissingLocked:
@@ -510,33 +390,25 @@
getContext().getText(R.string.lockscreen_missing_sim_message_short),
mPlmn);
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
- mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_sim_locked_message),
mPlmn);
- mEmergencyButtonEnabledBecauseSimLocked = true;
break;
case SimPukLocked:
carrierText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.lockscreen_sim_puk_locked_message),
mPlmn);
- if (!mLockPatternUtils.isPukUnlockScreenEnable()) {
- // This means we're showing the PUK unlock screen
- mEmergencyButtonEnabledBecauseSimLocked = true;
- }
break;
}
setCarrierText(carrierText);
setCarrierHelpText(carrierHelpTextId);
- updateEmergencyCallButtonState(mPhoneState);
}
-
/*
* Add emergencyCallMessage to carrier string only if phone supports emergency calls.
*/
@@ -608,21 +480,10 @@
}
}
- private void updateEmergencyCallButtonState(int phoneState) {
- if (mEmergencyCallButton != null) {
- boolean enabledBecauseSimLocked =
- mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked()
- && mEmergencyButtonEnabledBecauseSimLocked;
- boolean shown = mEmergencyCallButtonEnabledInScreen || enabledBecauseSimLocked;
- mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton,
- phoneState, shown);
- }
- }
-
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
- public void onRefreshBatteryInfo(BatteryStatus status) {
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
mPluggedIn = status.isPluggedIn();
mBatteryLevel = status.level;
@@ -645,23 +506,11 @@
}
@Override
- public void onPhoneStateChanged(int phoneState) {
- mPhoneState = phoneState;
- updateEmergencyCallButtonState(phoneState);
- }
-
- @Override
public void onSimStateChanged(IccCardConstants.State simState) {
updateCarrierStateWithSimStatus(simState);
}
};
- public void onClick(View v) {
- if (v == mEmergencyCallButton) {
- mCallback.takeEmergencyCallAction();
- }
- }
-
/**
* Performs concentenation of PLMN/SPN
* @param plmn
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
similarity index 81%
copy from policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 5c0cd4f..66c7c10 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -43,6 +43,7 @@
import com.android.internal.R;
import com.google.android.collect.Lists;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -52,8 +53,8 @@
*
* Note: under time crunch, this has been extended to include some stuff that
* doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns
- * the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()}
- * and {@link #clearFailedAttempts()}. Maybe we should rename this 'KeyguardContext'...
+ * the device, and {@link #getFailedUnlockAttempts()}, {@link #reportFailedAttempt()}
+ * and {@link #clearFailedUnlockAttempts()}. Maybe we should rename this 'KeyguardContext'...
*/
public class KeyguardUpdateMonitor {
@@ -76,6 +77,8 @@
protected static final int MSG_USER_SWITCHED = 310;
protected static final int MSG_USER_REMOVED = 311;
+ private static KeyguardUpdateMonitor sInstance;
+
private final Context mContext;
// Telephony state
@@ -85,16 +88,20 @@
private int mRingMode;
private int mPhoneState;
+ // Device provisioning state
private boolean mDeviceProvisioned;
+ // Battery status
private BatteryStatus mBatteryStatus;
+ // Password attempts
private int mFailedAttempts = 0;
private int mFailedBiometricUnlockAttempts = 0;
private boolean mClockVisible;
- private ArrayList<KeyguardUpdateMonitorCallback> mCallbacks = Lists.newArrayList();
+ private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
+ mCallbacks = Lists.newArrayList();
private ContentObserver mContentObserver;
private final Handler mHandler = new Handler() {
@@ -178,10 +185,10 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
}
}
};
@@ -254,7 +261,7 @@
}
/**
- * Determine whether the device is plugged in (USB or power).
+ * Determine whether the device is plugged in (USB, power, or wireless).
* @return true if the device is plugged in.
*/
boolean isPluggedIn() {
@@ -283,7 +290,14 @@
}
- public KeyguardUpdateMonitor(Context context) {
+ public static KeyguardUpdateMonitor getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new KeyguardUpdateMonitor(context);
+ }
+ return sInstance;
+ }
+
+ private KeyguardUpdateMonitor(Context context) {
mContext = context;
mDeviceProvisioned = Settings.Secure.getInt(
@@ -350,8 +364,11 @@
* Handle {@link #MSG_DPM_STATE_CHANGED}
*/
protected void handleDevicePolicyManagerStateChanged() {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onDevicePolicyManagerStateChanged();
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onDevicePolicyManagerStateChanged();
+ }
}
}
@@ -360,7 +377,10 @@
*/
protected void handleUserSwitched(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onUserSwitched(userId);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserSwitched(userId);
+ }
}
}
@@ -369,7 +389,10 @@
*/
protected void handleUserRemoved(int userId) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onUserRemoved(userId);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onUserRemoved(userId);
+ }
}
}
@@ -378,7 +401,10 @@
*/
protected void handleDeviceProvisioned() {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onDeviceProvisioned();
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onDeviceProvisioned();
+ }
}
if (mContentObserver != null) {
// We don't need the observer anymore...
@@ -400,7 +426,10 @@
mPhoneState = TelephonyManager.CALL_STATE_RINGING;
}
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onPhoneStateChanged(mPhoneState);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onPhoneStateChanged(mPhoneState);
+ }
}
}
@@ -411,7 +440,10 @@
if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
mRingMode = mode;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRingerModeChanged(mode);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRingerModeChanged(mode);
+ }
}
}
@@ -421,7 +453,10 @@
private void handleTimeUpdate() {
if (DEBUG) Log.d(TAG, "handleTimeUpdate");
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onTimeChanged();
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTimeChanged();
+ }
}
}
@@ -434,7 +469,10 @@
mBatteryStatus = status;
if (batteryUpdateInteresting) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRefreshBatteryInfo(status);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRefreshBatteryInfo(status);
+ }
}
}
}
@@ -447,7 +485,10 @@
+ ", spn = " + mTelephonySpn);
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ }
}
}
@@ -465,7 +506,10 @@
if (state != IccCardConstants.State.UNKNOWN && state != mSimState) {
mSimState = state;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onSimStateChanged(state);
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onSimStateChanged(state);
+ }
}
}
}
@@ -476,7 +520,10 @@
private void handleClockVisibilityChanged() {
if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()");
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onClockVisibilityChanged();
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onClockVisibilityChanged();
+ }
}
}
@@ -540,32 +587,46 @@
/**
* Remove the given observer's callback.
*
- * @param observer The observer to remove
+ * @param callback The callback to remove
*/
- public void removeCallback(Object observer) {
- mCallbacks.remove(observer);
+ public void removeCallback(KeyguardUpdateMonitorCallback callback) {
+ if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ if (mCallbacks.get(i).get() == callback) {
+ mCallbacks.remove(i);
+ }
+ }
}
/**
* Register to receive notifications about general keyguard information
* (see {@link InfoCallback}.
- * @param callback The callback.
+ * @param callback The callback to register
*/
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
- if (!mCallbacks.contains(callback)) {
- mCallbacks.add(callback);
- // Notify listener of the current state
- callback.onRefreshBatteryInfo(mBatteryStatus);
- callback.onTimeChanged();
- callback.onRingerModeChanged(mRingMode);
- callback.onPhoneStateChanged(mPhoneState);
- callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
- callback.onClockVisibilityChanged();
- callback.onSimStateChanged(mSimState);
- } else {
- if (DEBUG) Log.e(TAG, "Object tried to add another callback",
- new Exception("Called by"));
+ if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
+ // Prevent adding duplicate callbacks
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).get() == callback) {
+ if (DEBUG) Log.e(TAG, "Object tried to add another callback",
+ new Exception("Called by"));
+ return;
+ }
}
+ mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
+ removeCallback(null); // remove unused references
+ sendUpdates(callback);
+ }
+
+ private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
+ // Notify listener of the current state
+ callback.onRefreshBatteryInfo(mBatteryStatus);
+ callback.onTimeChanged();
+ callback.onRingerModeChanged(mRingMode);
+ callback.onPhoneStateChanged(mPhoneState);
+ callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ callback.onClockVisibilityChanged();
+ callback.onSimStateChanged(mSimState);
}
public void reportClockVisible(boolean visible) {
@@ -605,16 +666,16 @@
return mDeviceProvisioned;
}
- public int getFailedAttempts() {
+ public int getFailedUnlockAttempts() {
return mFailedAttempts;
}
- public void clearFailedAttempts() {
+ public void clearFailedUnlockAttempts() {
mFailedAttempts = 0;
mFailedBiometricUnlockAttempts = 0;
}
- public void reportFailedAttempt() {
+ public void reportFailedUnlockAttempt() {
mFailedAttempts++;
}
@@ -635,8 +696,23 @@
}
public boolean isSimLocked() {
- return mSimState == IccCardConstants.State.PIN_REQUIRED
- || mSimState == IccCardConstants.State.PUK_REQUIRED
- || mSimState == IccCardConstants.State.PERM_DISABLED;
+ return isSimLocked(mSimState);
+ }
+
+ public static boolean isSimLocked(IccCardConstants.State state) {
+ return state == IccCardConstants.State.PIN_REQUIRED
+ || state == IccCardConstants.State.PUK_REQUIRED
+ || state == IccCardConstants.State.PERM_DISABLED;
+ }
+
+ public boolean isSimPinSecure() {
+ return isSimPinSecure(mSimState);
+ }
+
+ public static boolean isSimPinSecure(IccCardConstants.State state) {
+ final IccCardConstants.State simState = state;
+ return (simState == IccCardConstants.State.PIN_REQUIRED
+ || simState == IccCardConstants.State.PUK_REQUIRED
+ || simState == IccCardConstants.State.PERM_DISABLED);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
similarity index 93%
copy from policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index d791419..3d65e68 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import android.app.admin.DevicePolicyManager;
import android.media.AudioManager;
-import com.android.internal.policy.impl.KeyguardUpdateMonitor.BatteryStatus;
import com.android.internal.telephony.IccCardConstants;
/**
@@ -31,7 +30,7 @@
*
* @param status current battery status
*/
- void onRefreshBatteryInfo(BatteryStatus status) { }
+ void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
/**
* Called once per minute or when the time changes.
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
similarity index 87%
copy from policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
index 29a5573..ad5de0e 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
@@ -29,15 +28,13 @@
import android.os.ServiceManager;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
-import android.view.View;
-import android.view.Gravity;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
/**
- * Base class for keyguard views. {@link #reset} is where you should
+ * Base class for keyguard view. {@link #reset} is where you should
* reset the state of your view. Use the {@link KeyguardViewCallback} via
* {@link #getCallback()} to send information back (such as poking the wake lock,
* or finishing the keyguard).
@@ -45,19 +42,20 @@
* Handles intercepting of media keys that still work when the keyguard is
* showing.
*/
-public abstract class KeyguardViewBase extends FrameLayout {
+public abstract class KeyguardViewBase extends LinearLayout {
private static final int BACKGROUND_COLOR = 0x70000000;
- private KeyguardViewCallback mCallback;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager = null;
+ protected KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
+
// Whether the volume keys should be handled by keyguard. If true, then
// they will be handled here for specific media types such as music, otherwise
// the audio service will bring up the volume dialog.
private static final boolean KEYGUARD_MANAGES_VOLUME = true;
// This is a faster way to draw the background on devices without hardware acceleration
- Drawable mBackgroundDrawable = new Drawable() {
+ private static final Drawable mBackgroundDrawable = new Drawable() {
@Override
public void draw(Canvas canvas) {
canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
@@ -77,18 +75,17 @@
}
};
- public KeyguardViewBase(Context context, KeyguardViewCallback callback) {
- super(context);
- mCallback = callback;
+ public KeyguardViewBase(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardViewBase(Context context, AttributeSet attrs) {
+ super(context, attrs);
resetBackground();
}
public void resetBackground() {
- setBackgroundDrawable(mBackgroundDrawable);
- }
-
- public KeyguardViewCallback getCallback() {
- return mCallback;
+ setBackground(mBackgroundDrawable);
}
/**
@@ -142,31 +139,12 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) {
- mCallback.pokeWakelock();
- }
-
if (interceptMediaKey(event)) {
return true;
}
return super.dispatchKeyEvent(event);
}
- private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) {
- if (event.getAction() != KeyEvent.ACTION_DOWN) {
- return false;
- }
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_UP:
- return false;
- default:
- return true;
- }
- }
-
/**
* Allows the media keys to work when the keyguard is showing.
* The media keys should be of no interest to the actual keyguard view(s),
@@ -267,4 +245,10 @@
super.dispatchSystemUiVisibilityChanged(visibility);
setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
}
+
+ public void setViewMediatorCallback(
+ KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
+ mViewMediatorCallback = viewMediatorCallback;
+ }
+
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
similarity index 72%
copy from policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index d521c05..61003bf 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -14,26 +14,27 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
-import com.android.internal.R;
-
+import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
-import android.graphics.Canvas;
import android.os.IBinder;
import android.os.SystemProperties;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import android.graphics.Color;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.R;
/**
* Manages creating, showing, hiding and resetting the keyguard. Calls back
@@ -41,24 +42,22 @@
* the wake lock and report that the keyguard is done, which is in turn,
* reported to this class by the current {@link KeyguardViewBase}.
*/
-public class KeyguardViewManager implements KeyguardWindowController {
+public class KeyguardViewManager {
private final static boolean DEBUG = false;
private static String TAG = "KeyguardViewManager";
private final Context mContext;
private final ViewManager mViewManager;
- private final KeyguardViewCallback mCallback;
- private final KeyguardViewProperties mKeyguardViewProperties;
-
- private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardViewMediator.ViewMediatorCallback mViewMediatorCallback;
private WindowManager.LayoutParams mWindowLayoutParams;
private boolean mNeedsInput = false;
private FrameLayout mKeyguardHost;
- private KeyguardViewBase mKeyguardView;
+ private KeyguardHostView mKeyguardView;
private boolean mScreenOn = false;
+ private LockPatternUtils mLockPatternUtils;
public interface ShowListener {
void onShown(IBinder windowToken);
@@ -68,34 +67,15 @@
* @param context Used to create views.
* @param viewManager Keyguard will be attached to this.
* @param callback Used to notify of changes.
+ * @param lockPatternUtils
*/
public KeyguardViewManager(Context context, ViewManager viewManager,
- KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties,
- KeyguardUpdateMonitor updateMonitor) {
+ KeyguardViewMediator.ViewMediatorCallback callback,
+ LockPatternUtils lockPatternUtils) {
mContext = context;
mViewManager = viewManager;
- mCallback = callback;
- mKeyguardViewProperties = keyguardViewProperties;
-
- mUpdateMonitor = updateMonitor;
- }
-
- /**
- * Helper class to host the keyguard view.
- */
- private static class KeyguardViewHost extends FrameLayout {
- private final KeyguardViewCallback mCallback;
-
- private KeyguardViewHost(Context context, KeyguardViewCallback callback) {
- super(context);
- mCallback = callback;
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- mCallback.keyguardDoneDrawing();
- }
+ mViewMediatorCallback = callback;
+ mLockPatternUtils = lockPatternUtils;
}
/**
@@ -105,30 +85,67 @@
public synchronized void show() {
if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
+ boolean enableScreenRotation = shouldEnableScreenRotation();
+
+ maybeCreateKeyguardLocked(enableScreenRotation);
+ maybeEnableScreenRotation(enableScreenRotation);
+
+ // Disable aspects of the system/status/navigation bars that are not appropriate or
+ // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities.
+ // Other disabled bits are handled by the KeyguardViewMediator talking directly to the
+ // status bar service.
+ int visFlags = View.STATUS_BAR_DISABLE_BACK | View.STATUS_BAR_DISABLE_HOME;
+ if (DEBUG) Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
+ mKeyguardHost.setSystemUiVisibility(visFlags);
+
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ mKeyguardHost.setVisibility(View.VISIBLE);
+ mKeyguardView.show();
+ mKeyguardView.requestFocus();
+ }
+
+ private boolean shouldEnableScreenRotation() {
Resources res = mContext.getResources();
- boolean enableScreenRotation =
- SystemProperties.getBoolean("lockscreen.rot_override",false)
- || res.getBoolean(R.bool.config_enableLockScreenRotation);
+ return SystemProperties.getBoolean("lockscreen.rot_override",false)
+ || res.getBoolean(com.android.internal.R.bool.config_enableLockScreenRotation);
+ }
+
+ class ViewManagerHost extends FrameLayout {
+ public ViewManagerHost(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation());
+ }
+ }
+
+ private void maybeCreateKeyguardLocked(boolean enableScreenRotation) {
+ final boolean isActivity = (mContext instanceof Activity); // for test activity
+
if (mKeyguardHost == null) {
if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
- mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
+ mKeyguardHost = new ViewManagerHost(mContext);
- final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- /*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
+ | WindowManager.LayoutParams.FLAG_SLIPPERY;
+
if (!mNeedsInput) {
flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
if (ActivityManager.isHighEndGfx()) {
flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
+
+ final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
+ final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION
+ : WindowManager.LayoutParams.TYPE_KEYGUARD;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD,
- flags, PixelFormat.TRANSLUCENT);
+ stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
if (ActivityManager.isHighEndGfx()) {
@@ -137,12 +154,31 @@
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
}
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;
- lp.setTitle("Keyguard");
+ lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");
mWindowLayoutParams = lp;
-
mViewManager.addView(mKeyguardHost, lp);
}
+ inflateKeyguardView();
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ }
+ private void inflateKeyguardView() {
+ if (mKeyguardView != null) {
+ mKeyguardHost.removeView(mKeyguardView);
+ }
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
+ mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
+ mKeyguardView.setLockPatternUtils(mLockPatternUtils);
+ mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);
+
+ if (mScreenOn) {
+ mKeyguardView.show();
+ }
+ }
+
+ private void maybeEnableScreenRotation(boolean enableScreenRotation) {
+ // TODO: move this outside
if (enableScreenRotation) {
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen On!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
@@ -150,40 +186,7 @@
if (DEBUG) Log.d(TAG, "Rotation sensor for lock screen Off!");
mWindowLayoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
}
-
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
-
- if (mKeyguardView == null) {
- if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
- mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mCallback,
- mUpdateMonitor, this);
- mKeyguardView.setId(R.id.lock_screen);
-
- final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
-
- mKeyguardHost.addView(mKeyguardView, lp);
-
- if (mScreenOn) {
- mKeyguardView.show();
- }
- }
-
- // Disable aspects of the system/status/navigation bars that are not appropriate or
- // useful for the lockscreen but can be re-shown by dialogs or SHOW_WHEN_LOCKED activities.
- // Other disabled bits are handled by the KeyguardViewMediator talking directly to the
- // status bar service.
- int visFlags =
- ( View.STATUS_BAR_DISABLE_BACK
- | View.STATUS_BAR_DISABLE_HOME
- );
- Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
- mKeyguardHost.setSystemUiVisibility(visFlags);
-
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
- mKeyguardHost.setVisibility(View.VISIBLE);
- mKeyguardView.requestFocus();
}
public void setNeedsInput(boolean needsInput) {
@@ -196,7 +199,13 @@
mWindowLayoutParams.flags |=
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
- mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+
+ try {
+ mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+ } catch (java.lang.IllegalArgumentException e) {
+ // TODO: Ensure this method isn't called on views that are changing...
+ Log.w(TAG,"Can't update input method on " + mKeyguardHost + " window not attached");
+ }
}
}
@@ -231,7 +240,7 @@
// Keyguard may be in the process of being shown, but not yet
// updated with the window manager... give it a chance to do so.
mKeyguardHost.post(new Runnable() {
- @Override public void run() {
+ public void run() {
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
showListener.onShown(mKeyguardHost.getWindowToken());
} else {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
similarity index 93%
copy from policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
copy to policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 236a4ea..3ee82f7 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.LockPatternUtils;
+import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -90,7 +91,7 @@
* directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
-public class KeyguardViewMediator implements KeyguardViewCallback {
+public class KeyguardViewMediator {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private final static boolean DEBUG = false;
private final static boolean DBG_WAKE = false;
@@ -211,8 +212,6 @@
private int mWakelockSequence;
- private PhoneWindowManager mCallback;
-
/**
* If the user has disabled the keyguard, then requests to exit, this is
* how we'll ultimately let them know whether it was successful. We use this
@@ -221,7 +220,6 @@
private WindowManagerPolicy.OnKeyguardExitResult mExitSecureCallback;
// the properties of the keyguard
- private KeyguardViewProperties mKeyguardViewProperties;
private KeyguardUpdateMonitor mUpdateMonitor;
@@ -252,6 +250,44 @@
*/
private final float mLockSoundVolume;
+ /**
+ * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
+ * various things.
+ */
+ public interface ViewMediatorCallback {
+
+ /**
+ * Request the wakelock to be poked for the default amount of time.
+ */
+ void pokeWakelock();
+
+ /**
+ * Request the wakelock to be poked for a specific amount of time.
+ * @param millis The amount of time in millis.
+ */
+ void pokeWakelock(long millis);
+
+ /**
+ * Report that the keyguard is done.
+ * @param authenticated Whether the user securely got past the keyguard.
+ * the only reason for this to be false is if the keyguard was instructed
+ * to appear temporarily to verify the user is supposed to get past the
+ * keyguard, and the user fails to do so.
+ */
+ void keyguardDone(boolean authenticated);
+
+ /**
+ * Report that the keyguard is done drawing.
+ */
+ void keyguardDoneDrawing();
+
+ /**
+ * Tell ViewMediator that the current view needs IME input
+ * @param needsInput
+ */
+ void setNeedsInput(boolean needsInput);
+ }
+
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -354,9 +390,51 @@
};
- public KeyguardViewMediator(Context context, PhoneWindowManager callback) {
+ ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
+ public void pokeWakelock() {
+ KeyguardViewMediator.this.pokeWakelock();
+ }
+
+ public void pokeWakelock(long holdMs) {
+ KeyguardViewMediator.this.pokeWakelock(holdMs);
+ }
+
+ public void keyguardDone(boolean authenticated) {
+ KeyguardViewMediator.this.keyguardDone(authenticated, true);
+ }
+
+ public void keyguardDoneDrawing() {
+ mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
+ }
+
+ @Override
+ public void setNeedsInput(boolean needsInput) {
+ mKeyguardViewManager.setNeedsInput(needsInput);
+ }
+ };
+
+ public void pokeWakelock() {
+ pokeWakelock(AWAKE_INTERVAL_DEFAULT_MS);
+ }
+
+ public void pokeWakelock(long holdMs) {
+ synchronized (this) {
+ if (DBG_WAKE) Log.d(TAG, "pokeWakelock(" + holdMs + ")");
+ mWakeLock.acquire();
+ mHandler.removeMessages(TIMEOUT);
+ mWakelockSequence++;
+ Message msg = mHandler.obtainMessage(TIMEOUT, mWakelockSequence, 0);
+ mHandler.sendMessageDelayed(msg, holdMs);
+ }
+ }
+
+ /**
+ * Construct a KeyguardViewMediator
+ * @param context
+ * @param lockPatternUtils optional mock interface for LockPatternUtils
+ */
+ public KeyguardViewMediator(Context context, LockPatternUtils lockPatternUtils) {
mContext = context;
- mCallback = callback;
mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPM.newWakeLock(
PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "keyguard");
@@ -371,15 +449,15 @@
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mUpdateMonitor = new KeyguardUpdateMonitor(context);
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
- mLockPatternUtils = new LockPatternUtils(mContext);
- mKeyguardViewProperties
- = new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);
+ mLockPatternUtils = lockPatternUtils != null
+ ? lockPatternUtils : new LockPatternUtils(mContext);
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- mKeyguardViewManager = new KeyguardViewManager(
- context, wm, this, mKeyguardViewProperties, mUpdateMonitor);
+
+ mKeyguardViewManager = new KeyguardViewManager(context, wm, mViewMediatorCallback,
+ mLockPatternUtils);
mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -526,7 +604,6 @@
synchronized (this) {
if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
-
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
@@ -796,7 +873,8 @@
}
public boolean isSecure() {
- return mKeyguardViewProperties.isSecure();
+ return mLockPatternUtils.isSecure()
+ || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure();
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -899,36 +977,6 @@
return true;
}
- /**
- * Callbacks from {@link KeyguardViewManager}.
- */
-
- /** {@inheritDoc} */
- public void pokeWakelock() {
- pokeWakelock(AWAKE_INTERVAL_DEFAULT_MS);
- }
-
- /** {@inheritDoc} */
- public void pokeWakelock(int holdMs) {
- synchronized (this) {
- if (DBG_WAKE) Log.d(TAG, "pokeWakelock(" + holdMs + ")");
- mWakeLock.acquire();
- mHandler.removeMessages(TIMEOUT);
- mWakelockSequence++;
- Message msg = mHandler.obtainMessage(TIMEOUT, mWakelockSequence, 0);
- mHandler.sendMessageDelayed(msg, holdMs);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see #handleKeyguardDone
- */
- public void keyguardDone(boolean authenticated) {
- keyguardDone(authenticated, true);
- }
-
public void keyguardDone(boolean authenticated, boolean wakeup) {
synchronized (this) {
EventLog.writeEvent(70000, 2);
@@ -938,7 +986,7 @@
mHandler.sendMessage(msg);
if (authenticated) {
- mUpdateMonitor.clearFailedAttempts();
+ mUpdateMonitor.clearFailedUnlockAttempts();
}
if (mExitSecureCallback != null) {
@@ -956,22 +1004,13 @@
}
/**
- * {@inheritDoc}
- *
- * @see #handleKeyguardDoneDrawing
- */
- public void keyguardDoneDrawing() {
- mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
- }
-
- /**
* This handler will be associated with the policy thread, which will also
* be the UI thread of the keyguard. Since the apis of the policy, and therefore
* this class, can be called by other threads, any action that directly
* interacts with the keyguard ui should be posted to this handler, rather
* than called directly.
*/
- private Handler mHandler = new Handler(true /*async*/) {
+ private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -1006,7 +1045,7 @@
handleKeyguardDoneDrawing();
return;
case KEYGUARD_DONE_AUTHENTICATING:
- keyguardDone(true);
+ keyguardDone(true, true);
return;
case SET_HIDDEN:
handleSetHidden(msg.arg1 != 0);
@@ -1032,8 +1071,10 @@
}
mWakeLock.release();
- final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
- mContext.sendBroadcastAsUser(mUserPresentIntent, currentUser);
+ if (!(mContext instanceof Activity)) {
+ final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
+ mContext.sendBroadcastAsUser(mUserPresentIntent, currentUser);
+ }
}
/**
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
new file mode 100644
index 0000000..f957753
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetFrame extends FrameLayout {
+ private final static PorterDuffXfermode sAddBlendMode =
+ new PorterDuffXfermode(PorterDuff.Mode.ADD);
+ private static int sWidgetPagePadding;
+ private static Drawable sLeftOverscrollDrawable;
+ private static Drawable sRightOverscrollDrawable;
+
+ private Drawable mForegroundDrawable;
+ private float mOverScrollAmount = 0f;
+ private final Rect mForegroundRect = new Rect();
+ private int mForegroundAlpha = 0;
+
+ public KeyguardWidgetFrame(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ if (sLeftOverscrollDrawable == null) {
+ Resources res = context.getResources();
+ sLeftOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_left);
+ sRightOverscrollDrawable = res.getDrawable(R.drawable.kg_widget_overscroll_layer_right);
+ sWidgetPagePadding = res.getDimensionPixelSize(R.dimen.kg_widget_page_padding);
+ }
+ setPadding(sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mForegroundAlpha > 0) {
+ mForegroundDrawable.setBounds(mForegroundRect);
+ Paint p = ((NinePatchDrawable) mForegroundDrawable).getPaint();
+ p.setXfermode(sAddBlendMode);
+ mForegroundDrawable.draw(canvas);
+ p.setXfermode(null);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mForegroundRect.set(sWidgetPagePadding, sWidgetPagePadding,
+ w - sWidgetPagePadding, h - sWidgetPagePadding);
+ }
+
+ void setOverScrollAmount(float r, boolean left) {
+ if (Float.compare(mOverScrollAmount, r) != 0) {
+ mOverScrollAmount = r;
+ if (left && mForegroundDrawable != sLeftOverscrollDrawable) {
+ mForegroundDrawable = sLeftOverscrollDrawable;
+ } else if (!left && mForegroundDrawable != sRightOverscrollDrawable) {
+ mForegroundDrawable = sRightOverscrollDrawable;
+ }
+
+ mForegroundAlpha = (int) Math.round((r * 255));
+ mForegroundDrawable.setAlpha(mForegroundAlpha);
+ invalidate();
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
new file mode 100644
index 0000000..fd9362a
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 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.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.animation.TimeInterpolator;
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import android.widget.FrameLayout;
+
+public class KeyguardWidgetPager extends PagedView {
+ ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
+ private static float CAMERA_DISTANCE = 10000;
+ private static float TRANSITION_SCALE_FACTOR = 0.74f;
+ private static float TRANSITION_PIVOT = 0.65f;
+ private static float TRANSITION_MAX_ROTATION = 30;
+ private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
+ private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
+ private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetPager(Context context) {
+ this(null, null, 0);
+ }
+
+ public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /*
+ * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+ */
+ public void addWidget(AppWidgetHostView widget) {
+ KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.CENTER;
+ // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+ // for the Keyguard, so we override it to be 0.
+ widget.setPadding(0, 0, 0, 0);
+ frame.addView(widget, lp);
+ addView(frame);
+ }
+
+ /*
+ * This interpolator emulates the rate at which the perceived scale of an object changes
+ * as its distance from a camera increases. When this interpolator is applied to a scale
+ * animation on a view, it evokes the sense that the object is shrinking due to moving away
+ * from the camera.
+ */
+ static class ZInterpolator implements TimeInterpolator {
+ private float focalLength;
+
+ public ZInterpolator(float foc) {
+ focalLength = foc;
+ }
+
+ public float getInterpolation(float input) {
+ return (1.0f - focalLength / (focalLength + input)) /
+ (1.0f - focalLength / (focalLength + 1.0f));
+ }
+ }
+
+ @Override
+ protected void overScroll(float amount) {
+ acceleratedOverScroll(amount);
+ }
+
+ // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
+ @Override
+ protected void screenScrolled(int screenCenter) {
+ super.screenScrolled(screenCenter);
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View v = getPageAt(i);
+ if (v != null) {
+ float scrollProgress = getScrollProgress(screenCenter, v, i);
+
+ float interpolatedProgress =
+ mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
+ float scale = (1 - interpolatedProgress) +
+ interpolatedProgress * TRANSITION_SCALE_FACTOR;
+ float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
+
+ float alpha;
+
+ if (scrollProgress < 0) {
+ alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
+ 1 - Math.abs(scrollProgress)) : 1.0f;
+ } else {
+ // On large screens we need to fade the page as it nears its leftmost position
+ alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
+ }
+
+ v.setCameraDistance(mDensity * CAMERA_DISTANCE);
+ int pageWidth = v.getMeasuredWidth();
+ int pageHeight = v.getMeasuredHeight();
+
+ if (PERFORM_OVERSCROLL_ROTATION) {
+ if (i == 0 && scrollProgress < 0) {
+ // Overscroll to the left
+ v.setPivotX(TRANSITION_PIVOT * pageWidth);
+ v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
+ if (v instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+ true);
+ }
+ scale = 1.0f;
+ alpha = 1.0f;
+ // On the first page, we don't want the page to have any lateral motion
+ translationX = 0;
+ } else if (i == getChildCount() - 1 && scrollProgress > 0) {
+ // Overscroll to the right
+ v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
+ v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
+ scale = 1.0f;
+ alpha = 1.0f;
+ if (v instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+ false);
+ }
+ // On the last page, we don't want the page to have any lateral motion.
+ translationX = 0;
+ } else {
+ v.setPivotY(pageHeight / 2.0f);
+ v.setPivotX(pageWidth / 2.0f);
+ v.setRotationY(0f);
+ if (v instanceof KeyguardWidgetFrame) {
+ ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
+ }
+ }
+ }
+
+ v.setTranslationX(translationX);
+ v.setScaleX(scale);
+ v.setScaleY(scale);
+ v.setAlpha(alpha);
+
+ // If the view has 0 alpha, we set it to be invisible so as to prevent
+ // it from accepting touches
+ if (alpha == 0) {
+ v.setVisibility(INVISIBLE);
+ } else if (v.getVisibility() != VISIBLE) {
+ v.setVisibility(VISIBLE);
+ }
+ }
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
new file mode 100644
index 0000000..1b46efa
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -0,0 +1,1704 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+
+/**
+ * An abstraction of the original Workspace which supports browsing through a
+ * sequential list of "pages"
+ */
+public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+ private static final String TAG = "WidgetPagedView";
+ private static final boolean DEBUG = false;
+ protected static final int INVALID_PAGE = -1;
+
+ // the min drag distance for a fling to register, to prevent random page shifts
+ private static final int MIN_LENGTH_FOR_FLING = 25;
+
+ protected static final int PAGE_SNAP_ANIMATION_DURATION = 550;
+ protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
+ protected static final float NANOTIME_DIV = 1000000000.0f;
+
+ private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
+
+ private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
+ // The page is moved more than halfway, automatically move to the next page on touch up.
+ private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
+
+ // The following constants need to be scaled based on density. The scaled versions will be
+ // assigned to the corresponding member variables below.
+ private static final int FLING_THRESHOLD_VELOCITY = 500;
+ private static final int MIN_SNAP_VELOCITY = 1500;
+ private static final int MIN_FLING_VELOCITY = 250;
+
+ static final int AUTOMATIC_PAGE_SPACING = -1;
+
+ protected int mFlingThresholdVelocity;
+ protected int mMinFlingVelocity;
+ protected int mMinSnapVelocity;
+
+ protected float mDensity;
+ protected float mSmoothingTime;
+ protected float mTouchX;
+
+ protected boolean mFirstLayout = true;
+
+ protected int mCurrentPage;
+ protected int mNextPage = INVALID_PAGE;
+ protected int mMaxScrollX;
+ protected Scroller mScroller;
+ private VelocityTracker mVelocityTracker;
+
+ private float mDownMotionX;
+ protected float mLastMotionX;
+ protected float mLastMotionXRemainder;
+ protected float mLastMotionY;
+ protected float mTotalMotionX;
+ private int mLastScreenCenter = -1;
+ private int[] mChildOffsets;
+ private int[] mChildRelativeOffsets;
+ private int[] mChildOffsetsWithLayoutScale;
+
+ protected final static int TOUCH_STATE_REST = 0;
+ protected final static int TOUCH_STATE_SCROLLING = 1;
+ protected final static int TOUCH_STATE_PREV_PAGE = 2;
+ protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+ protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
+
+ protected int mTouchState = TOUCH_STATE_REST;
+ protected boolean mForceScreenScrolled = false;
+
+ protected OnLongClickListener mLongClickListener;
+
+ protected boolean mAllowLongPress = true;
+
+ protected int mTouchSlop;
+ private int mPagingTouchSlop;
+ private int mMaximumVelocity;
+ private int mMinimumWidth;
+ protected int mPageSpacing;
+ protected int mPageLayoutPaddingTop;
+ protected int mPageLayoutPaddingBottom;
+ protected int mPageLayoutPaddingLeft;
+ protected int mPageLayoutPaddingRight;
+ protected int mPageLayoutWidthGap;
+ protected int mPageLayoutHeightGap;
+ protected int mCellCountX = 0;
+ protected int mCellCountY = 0;
+ protected boolean mCenterPagesVertically;
+ protected boolean mAllowOverScroll = true;
+ protected int mUnboundedScrollX;
+ protected int[] mTempVisiblePagesRange = new int[2];
+ protected boolean mForceDrawAllChildrenNextFrame;
+
+ // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
+ // it is equal to the scaled overscroll position. We use a separate value so as to prevent
+ // the screens from continuing to translate beyond the normal bounds.
+ protected int mOverScrollX;
+
+ // parameter that adjusts the layout to be optimized for pages with that scale factor
+ protected float mLayoutScale = 1.0f;
+
+ protected static final int INVALID_POINTER = -1;
+
+ protected int mActivePointerId = INVALID_POINTER;
+
+ private PageSwitchListener mPageSwitchListener;
+
+ protected ArrayList<Boolean> mDirtyPageContent;
+
+ // If true, syncPages and syncPageItems will be called to refresh pages
+ protected boolean mContentIsRefreshable = true;
+
+ // If true, modify alpha of neighboring pages as user scrolls left/right
+ protected boolean mFadeInAdjacentScreens = true;
+
+ // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
+ // to switch to a new page
+ protected boolean mUsePagingTouchSlop = true;
+
+ // If true, the subclass should directly update scrollX itself in its computeScroll method
+ // (SmoothPagedView does this)
+ protected boolean mDeferScrollUpdate = false;
+
+ protected boolean mIsPageMoving = false;
+
+ // All syncs and layout passes are deferred until data is ready.
+ protected boolean mIsDataReady = true;
+
+ // Scrolling indicator
+ private ValueAnimator mScrollIndicatorAnimator;
+ private View mScrollIndicator;
+ private int mScrollIndicatorPaddingLeft;
+ private int mScrollIndicatorPaddingRight;
+ private boolean mShouldShowScrollIndicator = false;
+ private boolean mShouldShowScrollIndicatorImmediately = false;
+ protected static final int sScrollIndicatorFadeInDuration = 150;
+ protected static final int sScrollIndicatorFadeOutDuration = 650;
+ protected static final int sScrollIndicatorFlashDuration = 650;
+
+ public interface PageSwitchListener {
+ void onPageSwitch(View newPage, int newPageIndex);
+ }
+
+ public PagedView(Context context) {
+ this(context, null);
+ }
+
+ public PagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.PagedView, defStyle, 0);
+ setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
+ mPageLayoutPaddingTop = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingTop, 0);
+ mPageLayoutPaddingBottom = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingBottom, 0);
+ mPageLayoutPaddingLeft = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingLeft, 0);
+ mPageLayoutPaddingRight = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingRight, 0);
+ mPageLayoutWidthGap = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutWidthGap, 0);
+ mPageLayoutHeightGap = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutHeightGap, 0);
+ mScrollIndicatorPaddingLeft =
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
+ mScrollIndicatorPaddingRight =
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
+ a.recycle();
+
+ setHapticFeedbackEnabled(false);
+ init();
+ }
+
+ /**
+ * Initializes various states for this workspace.
+ */
+ protected void init() {
+ mDirtyPageContent = new ArrayList<Boolean>();
+ mDirtyPageContent.ensureCapacity(32);
+ mScroller = new Scroller(getContext(), new ScrollInterpolator());
+ mCurrentPage = 0;
+ mCenterPagesVertically = true;
+
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mDensity = getResources().getDisplayMetrics().density;
+
+ mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
+ mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
+ mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
+ setOnHierarchyChangeListener(this);
+ }
+
+ public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
+ mPageSwitchListener = pageSwitchListener;
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ /**
+ * Called by subclasses to mark that data is ready, and that we can begin loading and laying
+ * out pages.
+ */
+ protected void setDataIsReady() {
+ mIsDataReady = true;
+ }
+
+ protected boolean isDataReady() {
+ return mIsDataReady;
+ }
+
+ /**
+ * Returns the index of the currently displayed page.
+ *
+ * @return The index of the currently displayed page.
+ */
+ int getCurrentPage() {
+ return mCurrentPage;
+ }
+
+ int getNextPage() {
+ return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
+ }
+
+ int getPageCount() {
+ return getChildCount();
+ }
+
+ View getPageAt(int index) {
+ return getChildAt(index);
+ }
+
+ protected int indexToPage(int index) {
+ return index;
+ }
+
+ /**
+ * Updates the scroll of the current page immediately to its final scroll position. We use this
+ * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
+ * the previous tab page.
+ */
+ protected void updateCurrentPageScroll() {
+ int offset = getChildOffset(mCurrentPage);
+ int relOffset = getRelativeChildOffset(mCurrentPage);
+ int newX = offset - relOffset;
+ scrollTo(newX, 0);
+ mScroller.setFinalX(newX);
+ mScroller.forceFinished(true);
+ }
+
+ /**
+ * Sets the current page.
+ */
+ void setCurrentPage(int currentPage) {
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+ // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
+ // the default
+ if (getChildCount() == 0) {
+ return;
+ }
+
+ mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+ updateCurrentPageScroll();
+ updateScrollingIndicator();
+ notifyPageSwitchListener();
+ invalidate();
+ }
+
+ protected void notifyPageSwitchListener() {
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ protected void pageBeginMoving() {
+ if (!mIsPageMoving) {
+ mIsPageMoving = true;
+ onPageBeginMoving();
+ }
+ }
+
+ protected void pageEndMoving() {
+ if (mIsPageMoving) {
+ mIsPageMoving = false;
+ onPageEndMoving();
+ }
+ }
+
+ protected boolean isPageMoving() {
+ return mIsPageMoving;
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void onPageBeginMoving() {
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void onPageEndMoving() {
+ }
+
+ /**
+ * Registers the specified listener on each page contained in this workspace.
+ *
+ * @param l The listener used to respond to long clicks.
+ */
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ mLongClickListener = l;
+ final int count = getPageCount();
+ for (int i = 0; i < count; i++) {
+ getPageAt(i).setOnLongClickListener(l);
+ }
+ }
+
+ @Override
+ public void scrollBy(int x, int y) {
+ scrollTo(mUnboundedScrollX + x, getScrollY() + y);
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ mUnboundedScrollX = x;
+
+ if (x < 0) {
+ super.scrollTo(0, y);
+ if (mAllowOverScroll) {
+ overScroll(x);
+ }
+ } else if (x > mMaxScrollX) {
+ super.scrollTo(mMaxScrollX, y);
+ if (mAllowOverScroll) {
+ overScroll(x - mMaxScrollX);
+ }
+ } else {
+ mOverScrollX = x;
+ super.scrollTo(x, y);
+ }
+
+ mTouchX = x;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ }
+
+ // we moved this functionality to a helper function so SmoothPagedView can reuse it
+ protected boolean computeScrollHelper() {
+ if (mScroller.computeScrollOffset()) {
+ // Don't bother scrolling if the page does not need to be moved
+ if (getScrollX() != mScroller.getCurrX()
+ || getScrollY() != mScroller.getCurrY()
+ || mOverScrollX != mScroller.getCurrX()) {
+ scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+ }
+ invalidate();
+ return true;
+ } else if (mNextPage != INVALID_PAGE) {
+ mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+ mNextPage = INVALID_PAGE;
+ notifyPageSwitchListener();
+
+ // We don't want to trigger a page end moving unless the page has settled
+ // and the user has stopped scrolling
+ if (mTouchState == TOUCH_STATE_REST) {
+ pageEndMoving();
+ }
+
+ // Notify the user when the page changes
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityManager.isEnabled()) {
+ AccessibilityEvent ev =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+ ev.getText().add(getCurrentPageDescription());
+ sendAccessibilityEventUnchecked(ev);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public String getCurrentPageDescription() {
+ return "";
+ }
+
+ @Override
+ public void computeScroll() {
+ computeScrollHelper();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!mIsDataReady || getChildCount() == 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ // Return early if we aren't given a proper dimension
+ if (widthSize <= 0 || heightSize <= 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ /* Allow the height to be set as WRAP_CONTENT. This allows the particular case
+ * of the All apps view on XLarge displays to not take up more space then it needs. Width
+ * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect
+ * each page to have the same width.
+ */
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+
+ // The children are given the same width and height as the workspace
+ // unless they were set to WRAP_CONTENT
+ if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ // disallowing padding in paged view (just pass 0)
+ final View child = getPageAt(i);
+
+ int childWidthMode = MeasureSpec.EXACTLY;
+ int childHeightMode = MeasureSpec.EXACTLY;
+
+ final int childWidthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
+ final int childHeightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+
+ // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
+ // We also wait until we set the measured dimensions before flushing the cache as well, to
+ // ensure that the cache is filled with good values.
+ invalidateCachedOffsets();
+
+ if (childCount > 0) {
+ if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ + getChildWidth(0));
+
+ // Calculate the variable page spacing if necessary
+ if (mPageSpacing == AUTOMATIC_PAGE_SPACING) {
+ // The gap between pages in the PagedView should be equal to the gap from the page
+ // to the edge of the screen (so it is not visible in the current screen). To
+ // account for unequal padding on each side of the paged view, we take the maximum
+ // of the left/right gap and use that as the gap between each page.
+ int offset = getRelativeChildOffset(0);
+ int spacing = Math.max(offset, widthSize - offset -
+ getChildAt(0).getMeasuredWidth());
+ setPageSpacing(spacing);
+ }
+ }
+
+ updateScrollingIndicatorPosition();
+
+ if (childCount > 0) {
+ mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
+ } else {
+ mMaxScrollX = 0;
+ }
+ }
+
+ public void setPageSpacing(int pageSpacing) {
+ mPageSpacing = pageSpacing;
+ invalidateCachedOffsets();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mIsDataReady || getChildCount() == 0) {
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
+ final int childCount = getChildCount();
+ int childLeft = getRelativeChildOffset(0);
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getPageAt(i);
+ if (child.getVisibility() != View.GONE) {
+ final int childWidth = getScaledMeasuredWidth(child);
+ final int childHeight = child.getMeasuredHeight();
+ int childTop = getPaddingTop();
+ if (mCenterPagesVertically) {
+ childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
+ }
+
+ if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
+ child.layout(childLeft, childTop,
+ childLeft + child.getMeasuredWidth(), childTop + childHeight);
+ childLeft += childWidth + mPageSpacing;
+ }
+ }
+
+ if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
+ setHorizontalScrollBarEnabled(false);
+ updateCurrentPageScroll();
+ setHorizontalScrollBarEnabled(true);
+ mFirstLayout = false;
+ }
+ }
+
+ protected void screenScrolled(int screenCenter) {
+ if (isScrollingIndicatorEnabled()) {
+ updateScrollingIndicator();
+ }
+ boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+
+ if (mFadeInAdjacentScreens && !isInOverscroll) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child != null) {
+ float scrollProgress = getScrollProgress(screenCenter, child, i);
+ float alpha = 1 - Math.abs(scrollProgress);
+ child.setAlpha(alpha);
+ }
+ }
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ // This ensures that when children are added, they get the correct transforms / alphas
+ // in accordance with any scroll effects.
+ mForceScreenScrolled = true;
+ invalidate();
+ invalidateCachedOffsets();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+
+ protected void invalidateCachedOffsets() {
+ int count = getChildCount();
+ if (count == 0) {
+ mChildOffsets = null;
+ mChildRelativeOffsets = null;
+ mChildOffsetsWithLayoutScale = null;
+ return;
+ }
+
+ mChildOffsets = new int[count];
+ mChildRelativeOffsets = new int[count];
+ mChildOffsetsWithLayoutScale = new int[count];
+ for (int i = 0; i < count; i++) {
+ mChildOffsets[i] = -1;
+ mChildRelativeOffsets[i] = -1;
+ mChildOffsetsWithLayoutScale[i] = -1;
+ }
+ }
+
+ protected int getChildOffset(int index) {
+ int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
+ mChildOffsets : mChildOffsetsWithLayoutScale;
+
+ if (childOffsets != null && childOffsets[index] != -1) {
+ return childOffsets[index];
+ } else {
+ if (getChildCount() == 0)
+ return 0;
+
+ int offset = getRelativeChildOffset(0);
+ for (int i = 0; i < index; ++i) {
+ offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
+ }
+ if (childOffsets != null) {
+ childOffsets[index] = offset;
+ }
+ return offset;
+ }
+ }
+
+ protected int getRelativeChildOffset(int index) {
+ if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) {
+ return mChildRelativeOffsets[index];
+ } else {
+ final int padding = getPaddingLeft() + getPaddingRight();
+ final int offset = getPaddingLeft() +
+ (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
+ if (mChildRelativeOffsets != null) {
+ mChildRelativeOffsets[index] = offset;
+ }
+ return offset;
+ }
+ }
+
+ protected int getScaledMeasuredWidth(View child) {
+ // This functions are called enough times that it actually makes a difference in the
+ // profiler -- so just inline the max() here
+ final int measuredWidth = child.getMeasuredWidth();
+ final int minWidth = mMinimumWidth;
+ final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth;
+ return (int) (maxWidth * mLayoutScale + 0.5f);
+ }
+
+ protected void getVisiblePages(int[] range) {
+ final int pageCount = getChildCount();
+
+ if (pageCount > 0) {
+ final int screenWidth = getMeasuredWidth();
+ int leftScreen = 0;
+ int rightScreen = 0;
+ View currPage = getPageAt(leftScreen);
+ while (leftScreen < pageCount - 1 &&
+ currPage.getX() + currPage.getWidth() -
+ currPage.getPaddingRight() < getScrollX()) {
+ leftScreen++;
+ currPage = getPageAt(leftScreen);
+ }
+ rightScreen = leftScreen;
+ currPage = getPageAt(rightScreen + 1);
+ while (rightScreen < pageCount - 1 &&
+ currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
+ rightScreen++;
+ currPage = getPageAt(rightScreen + 1);
+ }
+ range[0] = leftScreen;
+ range[1] = rightScreen;
+ } else {
+ range[0] = -1;
+ range[1] = -1;
+ }
+ }
+
+ protected boolean shouldDrawChild(View child) {
+ return child.getAlpha() > 0;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ int halfScreenSize = getMeasuredWidth() / 2;
+ // mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
+ // Otherwise it is equal to the scaled overscroll position.
+ int screenCenter = mOverScrollX + halfScreenSize;
+
+ if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
+ // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can
+ // set it for the next frame
+ mForceScreenScrolled = false;
+ screenScrolled(screenCenter);
+ mLastScreenCenter = screenCenter;
+ }
+
+ // Find out which screens are visible; as an optimization we only call draw on them
+ final int pageCount = getChildCount();
+ if (pageCount > 0) {
+ getVisiblePages(mTempVisiblePagesRange);
+ final int leftScreen = mTempVisiblePagesRange[0];
+ final int rightScreen = mTempVisiblePagesRange[1];
+ if (leftScreen != -1 && rightScreen != -1) {
+ final long drawingTime = getDrawingTime();
+ // Clip to the bounds
+ canvas.save();
+ canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
+ getScrollY() + getBottom() - getTop());
+
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final View v = getPageAt(i);
+ if (mForceDrawAllChildrenNextFrame ||
+ (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
+ drawChild(canvas, v, drawingTime);
+ }
+ }
+ mForceDrawAllChildrenNextFrame = false;
+ canvas.restore();
+ }
+ }
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ int page = indexToPage(indexOfChild(child));
+ if (page != mCurrentPage || !mScroller.isFinished()) {
+ snapToPage(page);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ int focusablePage;
+ if (mNextPage != INVALID_PAGE) {
+ focusablePage = mNextPage;
+ } else {
+ focusablePage = mCurrentPage;
+ }
+ View v = getPageAt(focusablePage);
+ if (v != null) {
+ return v.requestFocus(direction, previouslyFocusedRect);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ if (direction == View.FOCUS_LEFT) {
+ if (getCurrentPage() > 0) {
+ snapToPage(getCurrentPage() - 1);
+ return true;
+ }
+ } else if (direction == View.FOCUS_RIGHT) {
+ if (getCurrentPage() < getPageCount() - 1) {
+ snapToPage(getCurrentPage() + 1);
+ return true;
+ }
+ }
+ return super.dispatchUnhandledMove(focused, direction);
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
+ getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
+ }
+ if (direction == View.FOCUS_LEFT) {
+ if (mCurrentPage > 0) {
+ getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+ }
+ } else if (direction == View.FOCUS_RIGHT){
+ if (mCurrentPage < getPageCount() - 1) {
+ getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+ }
+ }
+ }
+
+ /**
+ * If one of our descendant views decides that it could be focused now, only
+ * pass that along if it's on the current page.
+ *
+ * This happens when live folders requery, and if they're off page, they
+ * end up calling requestFocus, which pulls it on page.
+ */
+ @Override
+ public void focusableViewAvailable(View focused) {
+ View current = getPageAt(mCurrentPage);
+ View v = focused;
+ while (true) {
+ if (v == current) {
+ super.focusableViewAvailable(focused);
+ return;
+ }
+ if (v == this) {
+ return;
+ }
+ ViewParent parent = v.getParent();
+ if (parent instanceof View) {
+ v = (View)v.getParent();
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (disallowIntercept) {
+ // We need to make sure to cancel our long press if
+ // a scrollable widget takes over touch events
+ final View currentPage = getPageAt(mCurrentPage);
+ currentPage.cancelLongPress();
+ }
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
+ /**
+ * Return true if a tap at (x, y) should trigger a flip to the previous page.
+ */
+ protected boolean hitsPreviousPage(float x, float y) {
+ return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+ }
+
+ /**
+ * Return true if a tap at (x, y) should trigger a flip to the next page.
+ */
+ protected boolean hitsNextPage(float x, float y) {
+ return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ /*
+ * This method JUST determines whether we want to intercept the motion.
+ * If we return true, onTouchEvent will be called and we do the actual
+ * scrolling there.
+ */
+ acquireVelocityTrackerAndAddMovement(ev);
+
+ // Skip touch handling if there are no pages to swipe
+ if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
+
+ /*
+ * Shortcut the most recurring case: the user is in the dragging
+ * state and he is moving his finger. We want to intercept this
+ * motion.
+ */
+ final int action = ev.getAction();
+ if ((action == MotionEvent.ACTION_MOVE) &&
+ (mTouchState == TOUCH_STATE_SCROLLING)) {
+ return true;
+ }
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ /*
+ * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+ * whether the user has moved far enough from his original down touch.
+ */
+ if (mActivePointerId != INVALID_POINTER) {
+ determineScrollingStart(ev);
+ break;
+ }
+ // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
+ // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
+ // i.e. fall through to the next case (don't break)
+ // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
+ // while it's small- this was causing a crash before we checked for INVALID_POINTER)
+ }
+
+ case MotionEvent.ACTION_DOWN: {
+ final float x = ev.getX();
+ final float y = ev.getY();
+ // Remember location of down touch
+ mDownMotionX = x;
+ mLastMotionX = x;
+ mLastMotionY = y;
+ mLastMotionXRemainder = 0;
+ mTotalMotionX = 0;
+ mActivePointerId = ev.getPointerId(0);
+ mAllowLongPress = true;
+
+ /*
+ * If being flinged and user touches the screen, initiate drag;
+ * otherwise don't. mScroller.isFinished should be false when
+ * being flinged.
+ */
+ final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
+ if (finishedScrolling) {
+ mTouchState = TOUCH_STATE_REST;
+ mScroller.abortAnimation();
+ } else {
+ mTouchState = TOUCH_STATE_SCROLLING;
+ }
+
+ // check if this can be the beginning of a tap on the side of the pages
+ // to scroll the current page
+ if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
+ if (getChildCount() > 0) {
+ if (hitsPreviousPage(x, y)) {
+ mTouchState = TOUCH_STATE_PREV_PAGE;
+ } else if (hitsNextPage(x, y)) {
+ mTouchState = TOUCH_STATE_NEXT_PAGE;
+ }
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mTouchState = TOUCH_STATE_REST;
+ mAllowLongPress = false;
+ mActivePointerId = INVALID_POINTER;
+ releaseVelocityTracker();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ releaseVelocityTracker();
+ break;
+ }
+
+ /*
+ * The only time we want to intercept motion events is if we are in the
+ * drag mode.
+ */
+ return mTouchState != TOUCH_STATE_REST;
+ }
+
+ protected void determineScrollingStart(MotionEvent ev) {
+ determineScrollingStart(ev, 1.0f);
+ }
+
+ /*
+ * Determines if we should change the touch state to start scrolling after the
+ * user moves their touch point too far.
+ */
+ protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
+ /*
+ * Locally do absolute value. mLastMotionX is set to the y value
+ * of the down event.
+ */
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ return;
+ }
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
+ final int xDiff = (int) Math.abs(x - mLastMotionX);
+ final int yDiff = (int) Math.abs(y - mLastMotionY);
+
+ final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
+ boolean xPaged = xDiff > mPagingTouchSlop;
+ boolean xMoved = xDiff > touchSlop;
+ boolean yMoved = yDiff > touchSlop;
+
+ if (xMoved || xPaged || yMoved) {
+ if (mUsePagingTouchSlop ? xPaged : xMoved) {
+ // Scroll if the user moved far enough along the X axis
+ mTouchState = TOUCH_STATE_SCROLLING;
+ mTotalMotionX += Math.abs(mLastMotionX - x);
+ mLastMotionX = x;
+ mLastMotionXRemainder = 0;
+ mTouchX = getScrollX();
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ pageBeginMoving();
+ }
+ // Either way, cancel any pending longpress
+ cancelCurrentPageLongPress();
+ }
+ }
+
+ protected void cancelCurrentPageLongPress() {
+ if (mAllowLongPress) {
+ mAllowLongPress = false;
+ // Try canceling the long press. It could also have been scheduled
+ // by a distant descendant, so use the mAllowLongPress flag to block
+ // everything
+ final View currentPage = getPageAt(mCurrentPage);
+ if (currentPage != null) {
+ currentPage.cancelLongPress();
+ }
+ }
+ }
+
+ protected float getScrollProgress(int screenCenter, View v, int page) {
+ final int halfScreenSize = getMeasuredWidth() / 2;
+
+ int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
+ int delta = screenCenter - (getChildOffset(page) -
+ getRelativeChildOffset(page) + halfScreenSize);
+
+ float scrollProgress = delta / (totalDistance * 1.0f);
+ scrollProgress = Math.min(scrollProgress, 1.0f);
+ scrollProgress = Math.max(scrollProgress, -1.0f);
+ return scrollProgress;
+ }
+
+ // This curve determines how the effect of scrolling over the limits of the page dimishes
+ // as the user pulls further and further from the bounds
+ private float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ protected void acceleratedOverScroll(float amount) {
+ int screenSize = getMeasuredWidth();
+
+ // We want to reach the max over scroll effect when the user has
+ // over scrolled half the size of the screen
+ float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
+
+ if (f == 0) return;
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ int overScrollAmount = (int) Math.round(f * screenSize);
+ if (amount < 0) {
+ mOverScrollX = overScrollAmount;
+ super.scrollTo(0, getScrollY());
+ } else {
+ mOverScrollX = mMaxScrollX + overScrollAmount;
+ super.scrollTo(mMaxScrollX, getScrollY());
+ }
+ invalidate();
+ }
+
+ protected void dampedOverScroll(float amount) {
+ int screenSize = getMeasuredWidth();
+
+ float f = (amount / screenSize);
+
+ if (f == 0) return;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
+ if (amount < 0) {
+ mOverScrollX = overScrollAmount;
+ super.scrollTo(0, getScrollY());
+ } else {
+ mOverScrollX = mMaxScrollX + overScrollAmount;
+ super.scrollTo(mMaxScrollX, getScrollY());
+ }
+ invalidate();
+ }
+
+ protected void overScroll(float amount) {
+ dampedOverScroll(amount);
+ }
+
+ protected float maxOverScroll() {
+ // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
+ // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
+ float f = 1.0f;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+ return OVERSCROLL_DAMP_FACTOR * f;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Skip touch handling if there are no pages to swipe
+ if (getChildCount() <= 0) return super.onTouchEvent(ev);
+
+ acquireVelocityTrackerAndAddMovement(ev);
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
+ // Remember where the motion event started
+ mDownMotionX = mLastMotionX = ev.getX();
+ mLastMotionXRemainder = 0;
+ mTotalMotionX = 0;
+ mActivePointerId = ev.getPointerId(0);
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ pageBeginMoving();
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ // Scroll to follow the motion event
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final float deltaX = mLastMotionX + mLastMotionXRemainder - x;
+
+ mTotalMotionX += Math.abs(deltaX);
+
+ // Only scroll and update mLastMotionX if we have moved some discrete amount. We
+ // keep the remainder because we are actually testing if we've moved from the last
+ // scrolled position (which is discrete).
+ if (Math.abs(deltaX) >= 1.0f) {
+ mTouchX += deltaX;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ if (!mDeferScrollUpdate) {
+ scrollBy((int) deltaX, 0);
+ if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);
+ } else {
+ invalidate();
+ }
+ mLastMotionX = x;
+ mLastMotionXRemainder = deltaX - (int) deltaX;
+ } else {
+ awakenScrollBars();
+ }
+ } else {
+ determineScrollingStart(ev);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ final int activePointerId = mActivePointerId;
+ final int pointerIndex = ev.findPointerIndex(activePointerId);
+ final float x = ev.getX(pointerIndex);
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+ final int deltaX = (int) (x - mDownMotionX);
+ final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage));
+ boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
+ SIGNIFICANT_MOVE_THRESHOLD;
+
+ mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
+
+ boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
+ Math.abs(velocityX) > mFlingThresholdVelocity;
+
+ // In the case that the page is moved far to one direction and then is flung
+ // in the opposite direction, we use a threshold to determine whether we should
+ // just return to the starting page, or if we should skip one further.
+ boolean returnToOriginalPage = false;
+ if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
+ Math.signum(velocityX) != Math.signum(deltaX) && isFling) {
+ returnToOriginalPage = true;
+ }
+
+ int finalPage;
+ // We give flings precedence over large moves, which is why we short-circuit our
+ // test for a large move if a fling has been registered. That is, a large
+ // move to the left and fling to the right will register as a fling to the right.
+ if (((isSignificantMove && deltaX > 0 && !isFling) ||
+ (isFling && velocityX > 0)) && mCurrentPage > 0) {
+ finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+ snapToPageWithVelocity(finalPage, velocityX);
+ } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
+ (isFling && velocityX < 0)) &&
+ mCurrentPage < getChildCount() - 1) {
+ finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+ snapToPageWithVelocity(finalPage, velocityX);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.max(0, mCurrentPage - 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ } else {
+ onUnhandledTap(ev);
+ }
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ releaseVelocityTracker();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ snapToDestination();
+ }
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ releaseVelocityTracker();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_SCROLL: {
+ // Handle mouse (or ext. device) by shifting the page depending on the scroll
+ final float vscroll;
+ final float hscroll;
+ if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
+ vscroll = 0;
+ hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ } else {
+ vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ }
+ if (hscroll != 0 || vscroll != 0) {
+ if (hscroll > 0 || vscroll > 0) {
+ scrollRight();
+ } else {
+ scrollLeft();
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
+ private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ }
+
+ private void releaseVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ // TODO: Make this decision more intelligent.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
+ mLastMotionY = ev.getY(newPointerIndex);
+ mLastMotionXRemainder = 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {}
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ int page = indexToPage(indexOfChild(child));
+ if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
+ snapToPage(page);
+ }
+ }
+
+ protected int getChildIndexForRelativeOffset(int relativeOffset) {
+ final int childCount = getChildCount();
+ int left;
+ int right;
+ for (int i = 0; i < childCount; ++i) {
+ left = getRelativeChildOffset(i);
+ right = (left + getScaledMeasuredWidth(getPageAt(i)));
+ if (left <= relativeOffset && relativeOffset <= right) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ protected int getChildWidth(int index) {
+ // This functions are called enough times that it actually makes a difference in the
+ // profiler -- so just inline the max() here
+ final int measuredWidth = getPageAt(index).getMeasuredWidth();
+ final int minWidth = mMinimumWidth;
+ return (minWidth > measuredWidth) ? minWidth : measuredWidth;
+ }
+
+ int getPageNearestToCenterOfScreen() {
+ int minDistanceFromScreenCenter = Integer.MAX_VALUE;
+ int minDistanceFromScreenCenterIndex = -1;
+ int screenCenter = getScrollX() + (getMeasuredWidth() / 2);
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View layout = (View) getPageAt(i);
+ int childWidth = getScaledMeasuredWidth(layout);
+ int halfChildWidth = (childWidth / 2);
+ int childCenter = getChildOffset(i) + halfChildWidth;
+ int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+ if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
+ minDistanceFromScreenCenter = distanceFromScreenCenter;
+ minDistanceFromScreenCenterIndex = i;
+ }
+ }
+ return minDistanceFromScreenCenterIndex;
+ }
+
+ protected void snapToDestination() {
+ snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+ }
+
+ private static class ScrollInterpolator implements Interpolator {
+ public ScrollInterpolator() {
+ }
+
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t*t*t*t*t + 1;
+ }
+ }
+
+ // We want the duration of the page snap animation to be influenced by the distance that
+ // the screen has to travel, however, we don't want this duration to be effected in a
+ // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+ // of travel has on the overall snap duration.
+ float distanceInfluenceForSnapDuration(float f) {
+ f -= 0.5f; // center the values about 0.
+ f *= 0.3f * Math.PI / 2.0f;
+ return (float) Math.sin(f);
+ }
+
+ protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+ int halfScreenSize = getMeasuredWidth() / 2;
+
+ if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
+ if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
+ + getMeasuredWidth() + ", " + getChildWidth(whichPage));
+ final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ int delta = newX - mUnboundedScrollX;
+ int duration = 0;
+
+ if (Math.abs(velocity) < mMinFlingVelocity) {
+ // If the velocity is low enough, then treat this more as an automatic page advance
+ // as opposed to an apparent physical response to flinging
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ return;
+ }
+
+ // Here we compute a "distance" that will be used in the computation of the overall
+ // snap duration. This is a function of the actual distance that needs to be traveled;
+ // we keep this value close to half screen size in order to reduce the variance in snap
+ // duration as a function of the distance the page needs to travel.
+ float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize));
+ float distance = halfScreenSize + halfScreenSize *
+ distanceInfluenceForSnapDuration(distanceRatio);
+
+ velocity = Math.abs(velocity);
+ velocity = Math.max(mMinSnapVelocity, velocity);
+
+ // we want the page's snap velocity to approximately match the velocity at which the
+ // user flings, so we scale the duration by a value near to the derivative of the scroll
+ // interpolator at zero, ie. 5. We use 4 to make it a little slower.
+ duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+
+ snapToPage(whichPage, delta, duration);
+ }
+
+ protected void snapToPage(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ }
+
+ protected void snapToPage(int whichPage, int duration) {
+ whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+
+ if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
+ if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ + getChildWidth(whichPage));
+ int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ final int sX = mUnboundedScrollX;
+ final int delta = newX - sX;
+ snapToPage(whichPage, delta, duration);
+ }
+
+ protected void snapToPage(int whichPage, int delta, int duration) {
+ mNextPage = whichPage;
+
+ View focusedChild = getFocusedChild();
+ if (focusedChild != null && whichPage != mCurrentPage &&
+ focusedChild == getPageAt(mCurrentPage)) {
+ focusedChild.clearFocus();
+ }
+
+ pageBeginMoving();
+ awakenScrollBars(duration);
+ if (duration == 0) {
+ duration = Math.abs(delta);
+ }
+
+ if (!mScroller.isFinished()) mScroller.abortAnimation();
+ mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
+
+ notifyPageSwitchListener();
+ invalidate();
+ }
+
+ public void scrollLeft() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
+ } else {
+ if (mNextPage > 0) snapToPage(mNextPage - 1);
+ }
+ }
+
+ public void scrollRight() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
+ } else {
+ if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
+ }
+ }
+
+ public int getPageForView(View v) {
+ int result = -1;
+ if (v != null) {
+ ViewParent vp = v.getParent();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ if (vp == getPageAt(i)) {
+ return i;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return True is long presses are still allowed for the current touch
+ */
+ public boolean allowLongPress() {
+ return mAllowLongPress;
+ }
+
+ /**
+ * Set true to allow long-press events to be triggered, usually checked by
+ * {@link Launcher} to accept or block dpad-initiated long-presses.
+ */
+ public void setAllowLongPress(boolean allowLongPress) {
+ mAllowLongPress = allowLongPress;
+ }
+
+ public static class SavedState extends BaseSavedState {
+ int currentPage = -1;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPage = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(currentPage);
+ }
+
+ 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];
+ }
+ };
+ }
+
+ protected View getScrollingIndicator() {
+ return null;
+ }
+
+ protected boolean isScrollingIndicatorEnabled() {
+ return false;
+ }
+
+ Runnable hideScrollingIndicatorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hideScrollingIndicator(false);
+ }
+ };
+
+ protected void flashScrollingIndicator(boolean animated) {
+ removeCallbacks(hideScrollingIndicatorRunnable);
+ showScrollingIndicator(!animated);
+ postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration);
+ }
+
+ protected void showScrollingIndicator(boolean immediately) {
+ mShouldShowScrollIndicator = true;
+ mShouldShowScrollIndicatorImmediately = true;
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ mShouldShowScrollIndicator = false;
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ // Fade the indicator in
+ updateScrollingIndicatorPosition();
+ mScrollIndicator.setVisibility(View.VISIBLE);
+ cancelScrollingIndicatorAnimations();
+ if (immediately) {
+ mScrollIndicator.setAlpha(1f);
+ } else {
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f);
+ mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration);
+ mScrollIndicatorAnimator.start();
+ }
+ }
+ }
+
+ protected void cancelScrollingIndicatorAnimations() {
+ if (mScrollIndicatorAnimator != null) {
+ mScrollIndicatorAnimator.cancel();
+ }
+ }
+
+ protected void hideScrollingIndicator(boolean immediately) {
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ // Fade the indicator out
+ updateScrollingIndicatorPosition();
+ cancelScrollingIndicatorAnimations();
+ if (immediately) {
+ mScrollIndicator.setVisibility(View.INVISIBLE);
+ mScrollIndicator.setAlpha(0f);
+ } else {
+ mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
+ mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration);
+ mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean cancelled = false;
+ @Override
+ public void onAnimationCancel(android.animation.Animator animation) {
+ cancelled = true;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!cancelled) {
+ mScrollIndicator.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ mScrollIndicatorAnimator.start();
+ }
+ }
+ }
+
+ /**
+ * To be overridden by subclasses to determine whether the scroll indicator should stretch to
+ * fill its space on the track or not.
+ */
+ protected boolean hasElasticScrollIndicator() {
+ return true;
+ }
+
+ private void updateScrollingIndicator() {
+ if (getChildCount() <= 1) return;
+ if (!isScrollingIndicatorEnabled()) return;
+
+ getScrollingIndicator();
+ if (mScrollIndicator != null) {
+ updateScrollingIndicatorPosition();
+ }
+ if (mShouldShowScrollIndicator) {
+ showScrollingIndicator(mShouldShowScrollIndicatorImmediately);
+ }
+ }
+
+ private void updateScrollingIndicatorPosition() {
+ if (!isScrollingIndicatorEnabled()) return;
+ if (mScrollIndicator == null) return;
+ int numPages = getChildCount();
+ int pageWidth = getMeasuredWidth();
+ int lastChildIndex = Math.max(0, getChildCount() - 1);
+ int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
+ int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
+ int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
+ mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
+
+ float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
+ int indicatorSpace = trackWidth / numPages;
+ int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
+ if (hasElasticScrollIndicator()) {
+ if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) {
+ mScrollIndicator.getLayoutParams().width = indicatorSpace;
+ mScrollIndicator.requestLayout();
+ }
+ } else {
+ int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2;
+ indicatorPos += indicatorCenterOffset;
+ }
+ mScrollIndicator.setTranslationX(indicatorPos);
+ }
+
+ /* Accessibility */
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setScrollable(getPageCount() > 1);
+ if (getCurrentPage() < getPageCount() - 1) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ }
+ if (getCurrentPage() > 0) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setScrollable(true);
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+ event.setFromIndex(mCurrentPage);
+ event.setToIndex(mCurrentPage);
+ event.setItemCount(getChildCount());
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (super.performAccessibilityAction(action, arguments)) {
+ return true;
+ }
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ if (getCurrentPage() < getPageCount() - 1) {
+ scrollRight();
+ return true;
+ }
+ } break;
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ if (getCurrentPage() > 0) {
+ scrollLeft();
+ return true;
+ }
+ } break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onHoverEvent(android.view.MotionEvent event) {
+ return true;
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/AccountUnlockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/AccountUnlockScreen.java
index a4baeed..d6a31b8 100644
--- a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/AccountUnlockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
diff --git a/policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/BiometricSensorUnlock.java
similarity index 97%
copy from policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java
copy to policy/src/com/android/internal/policy/impl/keyguard_obsolete/BiometricSensorUnlock.java
index f476f82..c38525e 100644
--- a/policy/src/com/android/internal/policy/impl/BiometricSensorUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/BiometricSensorUnlock.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.view.View;
diff --git a/policy/src/com/android/internal/policy/impl/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/FaceUnlock.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/FaceUnlock.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/FaceUnlock.java
index fda3c9d..e4d9215 100644
--- a/policy/src/com/android/internal/policy/impl/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/FaceUnlock.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
import com.android.internal.policy.IFaceLockCallback;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreen.java
similarity index 94%
rename from policy/src/com/android/internal/policy/impl/KeyguardScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreen.java
index bbb6875..ba5b7ff 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
/**
* Common interface of each {@link android.view.View} that is a screen of
@@ -27,7 +27,7 @@
* keyboard to be displayed.
*/
boolean needsInput();
-
+
/**
* This screen is no longer in front of the user.
*/
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardScreenCallback.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreenCallback.java
similarity index 97%
rename from policy/src/com/android/internal/policy/impl/KeyguardScreenCallback.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreenCallback.java
index a843603..be505a1 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardScreenCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardScreenCallback.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.content.res.Configuration;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
similarity index 98%
rename from policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
index bb07a1d..b6ffde0 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.DigitalClock;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TransportControlView;
-import com.android.internal.policy.impl.KeyguardUpdateMonitor.BatteryStatus;
import java.util.ArrayList;
import java.util.Date;
@@ -180,7 +179,7 @@
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
@@ -621,8 +620,7 @@
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onRefreshBatteryInfo(BatteryStatus status) {
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
mPluggedIn = status.isPluggedIn();
mBatteryLevel = status.level;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitor.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitor.java
index 5c0cd4f..d990f5f 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitorCallback.java
similarity index 93%
rename from policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitorCallback.java
index d791419..79233e8 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardUpdateMonitorCallback.java
@@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.app.admin.DevicePolicyManager;
import android.media.AudioManager;
-import com.android.internal.policy.impl.KeyguardUpdateMonitor.BatteryStatus;
import com.android.internal.telephony.IccCardConstants;
/**
@@ -31,7 +30,7 @@
*
* @param status current battery status
*/
- void onRefreshBatteryInfo(BatteryStatus status) { }
+ void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
/**
* Called once per minute or when the time changes.
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewBase.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewBase.java
index 29a5573..f9fe797 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewBase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.content.Context;
import android.content.Intent;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewCallback.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewCallback.java
similarity index 94%
rename from policy/src/com/android/internal/policy/impl/KeyguardViewCallback.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewCallback.java
index b376d65..4cc0f30 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewCallback.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
/**
- * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
+ * The callback used by the keyguard view to tell the {@link KeyguardViewMediator}
* various things.
*/
public interface KeyguardViewCallback {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewManager.java
similarity index 98%
rename from policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewManager.java
index d521c05..5dbef48 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
@@ -231,7 +231,7 @@
// Keyguard may be in the process of being shown, but not yet
// updated with the window manager... give it a chance to do so.
mKeyguardHost.post(new Runnable() {
- @Override public void run() {
+ public void run() {
if (mKeyguardHost.getVisibility() == View.VISIBLE) {
showListener.onShown(mKeyguardHost.getWindowToken());
} else {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
index 236a4ea..8ff8dad 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewMediator.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import com.android.internal.policy.impl.PhoneWindowManager;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.LockPatternUtils;
@@ -292,7 +293,7 @@
@Override
public void onDeviceProvisioned() {
- mContext.sendBroadcast(mUserPresentIntent);
+ mContext.sendBroadcastAsUser(mUserPresentIntent, UserHandle.ALL);
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewProperties.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewProperties.java
similarity index 96%
rename from policy/src/com/android/internal/policy/impl/KeyguardViewProperties.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewProperties.java
index 51b7f1e..676574d 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewProperties.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardViewProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.content.Context;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardWindowController.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardWindowController.java
similarity index 93%
rename from policy/src/com/android/internal/policy/impl/KeyguardWindowController.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardWindowController.java
index 4ad48fb..98e3209 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardWindowController.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardWindowController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
/**
* Interface passed to the keyguard view, for it to call up to control
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
index 32aec10..4dc83b6 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
-import com.android.internal.policy.impl.KeyguardUpdateMonitor.BatteryStatus;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockScreenWidgetCallback;
@@ -689,7 +688,7 @@
KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
- public void onRefreshBatteryInfo(BatteryStatus status) {
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
// When someone plugs in or unplugs the device, we hide the biometric sensor area and
// suppress its startup for the next onScreenTurnedOn(). Since plugging/unplugging
// causes the screen to turn on, the biometric unlock would start if it wasn't
@@ -1014,7 +1013,7 @@
// TODO: make faceLockAreaView a more general biometricUnlockView
// We will need to add our Face Unlock specific child views programmatically in
// initializeView rather than having them in the XML files.
- View biometricUnlockView = view.findViewById(R.id.faceLockAreaView);
+ View biometricUnlockView = view.findViewById(R.id.face_unlock_area_view);
if (biometricUnlockView != null) {
mBiometricUnlock = new FaceUnlock(mContext, mUpdateMonitor, mLockPatternUtils,
mKeyguardScreenCallback);
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewProperties.java
similarity index 97%
rename from policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewProperties.java
index 5066e3c..5d9cc8e 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.widget.LockPatternUtils;
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/LockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockScreen.java
index 91b5ca1..4e9a1f7 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import com.android.internal.R;
import com.android.internal.telephony.IccCardConstants;
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/PasswordUnlockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/PasswordUnlockScreen.java
index 203f9db..87a7371 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/PasswordUnlockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import java.util.List;
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/PatternUnlockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/PatternUnlockScreen.java
index 9a6d2cc..6d5706b 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/PatternUnlockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimPukUnlockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimPukUnlockScreen.java
index 3b2a473..3c1703a 100644
--- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimPukUnlockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.app.Dialog;
import android.app.ProgressDialog;
diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimUnlockScreen.java
similarity index 99%
rename from policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimUnlockScreen.java
index 80407f5..13c040c 100644
--- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/SimUnlockScreen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.app.Dialog;
import android.app.ProgressDialog;
diff --git a/policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java b/policy/tests/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewTest.java
similarity index 96%
rename from policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
rename to policy/tests/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewTest.java
index 862e683..97c5672 100644
--- a/policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
+++ b/policy/tests/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardViewTest.java
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package com.android.internal.policy.impl;
+package com.android.internal.policy.impl.keyguard_obsolete;
import android.content.Context;
-import com.android.internal.policy.impl.KeyguardViewCallback;
+
+import com.android.internal.policy.impl.keyguard_obsolete.KeyguardScreen;
+import com.android.internal.policy.impl.keyguard_obsolete.KeyguardUpdateMonitor;
+import com.android.internal.policy.impl.keyguard_obsolete.KeyguardViewCallback;
+import com.android.internal.policy.impl.keyguard_obsolete.KeyguardWindowController;
+import com.android.internal.policy.impl.keyguard_obsolete.LockPatternKeyguardView;
import com.android.internal.telephony.IccCardConstants;
import android.content.res.Configuration;
import android.test.AndroidTestCase;
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index b15d4c8..1f3d2cf 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -298,7 +298,7 @@
}
if (becomingVisible) {
- status = update.state.surfaceControl->show(surfaceLayer);
+ status = update.state.surfaceControl->show();
if (status) {
ALOGE("Error %d showing sprite surface.", status);
} else {
@@ -369,7 +369,8 @@
ensureSurfaceComposerClient();
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
- String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
+ String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden);
if (surfaceControl == NULL || !surfaceControl->isValid()
|| !surfaceControl->getSurface()->isValid()) {
ALOGE("Error creating sprite surface.");
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 32ac8e1..f4ad756 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -34,6 +34,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
import android.text.format.Time;
@@ -242,32 +243,39 @@
"android.permission.SET_TIME_ZONE",
"setTimeZone");
- if (TextUtils.isEmpty(tz)) return;
- TimeZone zone = TimeZone.getTimeZone(tz);
- // Prevent reentrant calls from stepping on each other when writing
- // the time zone property
- boolean timeZoneWasChanged = false;
- synchronized (this) {
- String current = SystemProperties.get(TIMEZONE_PROPERTY);
- if (current == null || !current.equals(zone.getID())) {
- if (localLOGV) Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
- timeZoneWasChanged = true;
- SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
- }
-
- // Update the kernel timezone information
- // Kernel tracks time offsets as 'minutes west of GMT'
- int gmtOffset = zone.getOffset(System.currentTimeMillis());
- setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
- }
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ if (TextUtils.isEmpty(tz)) return;
+ TimeZone zone = TimeZone.getTimeZone(tz);
+ // Prevent reentrant calls from stepping on each other when writing
+ // the time zone property
+ boolean timeZoneWasChanged = false;
+ synchronized (this) {
+ String current = SystemProperties.get(TIMEZONE_PROPERTY);
+ if (current == null || !current.equals(zone.getID())) {
+ if (localLOGV) {
+ Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID());
+ }
+ timeZoneWasChanged = true;
+ SystemProperties.set(TIMEZONE_PROPERTY, zone.getID());
+ }
- TimeZone.setDefault(null);
-
- if (timeZoneWasChanged) {
- Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time-zone", zone.getID());
- mContext.sendBroadcast(intent);
+ // Update the kernel timezone information
+ // Kernel tracks time offsets as 'minutes west of GMT'
+ int gmtOffset = zone.getOffset(System.currentTimeMillis());
+ setKernelTimezone(mDescriptor, -(gmtOffset / 60000));
+ }
+
+ TimeZone.setDefault(null);
+
+ if (timeZoneWasChanged) {
+ Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", zone.getID());
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
}
@@ -303,7 +311,7 @@
}
}
}
-
+
public void removeLocked(String packageName) {
removeLocked(mRtcWakeupAlarms, packageName);
removeLocked(mRtcAlarms, packageName);
@@ -327,6 +335,29 @@
}
}
}
+
+ public void removeUserLocked(int userHandle) {
+ removeUserLocked(mRtcWakeupAlarms, userHandle);
+ removeUserLocked(mRtcAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
+ removeUserLocked(mElapsedRealtimeAlarms, userHandle);
+ }
+
+ private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
+ if (alarmList.size() <= 0) {
+ return;
+ }
+
+ // iterator over the list removing any it where the intent match
+ Iterator<Alarm> it = alarmList.iterator();
+
+ while (it.hasNext()) {
+ Alarm alarm = it.next();
+ if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) {
+ it.remove();
+ }
+ }
+ }
public boolean lookForPackageLocked(String packageName) {
return lookForPackageLocked(mRtcWakeupAlarms, packageName)
@@ -637,7 +668,7 @@
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
synchronized (mLock) {
@@ -822,6 +853,7 @@
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ sdFilter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(this, sdFilter);
}
@@ -841,6 +873,11 @@
return;
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ removeUserLocked(userHandle);
+ }
} else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
&& intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 8e341df..343e70d 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,10 +27,10 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -150,13 +150,13 @@
// Register for the boot completed broadcast, so we can send the
// ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
- mContext.registerReceiver(mBroadcastReceiver,
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
- Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -165,12 +165,14 @@
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ filter, null, null);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ sdFilter, null, null);
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -180,60 +182,75 @@
onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
}, userFilter);
+
+ IntentFilter userStopFilter = new IntentFilter();
+ userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserStopped(getSendingUserId());
+ }
+ }, UserHandle.ALL, userFilter, null, null);
}
@Override
public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
- return getImplForUser().allocateAppWidgetId(packageName, hostId);
+ return getImplForUser(UserHandle.getCallingUserId()).allocateAppWidgetId(
+ packageName, hostId);
}
@Override
public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
- getImplForUser().deleteAppWidgetId(appWidgetId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteAppWidgetId(appWidgetId);
}
@Override
public void deleteHost(int hostId) throws RemoteException {
- getImplForUser().deleteHost(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteHost(hostId);
}
@Override
public void deleteAllHosts() throws RemoteException {
- getImplForUser().deleteAllHosts();
+ getImplForUser(UserHandle.getCallingUserId()).deleteAllHosts();
}
@Override
public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
- getImplForUser().bindAppWidgetId(appWidgetId, provider);
+ getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider);
}
@Override
public boolean bindAppWidgetIdIfAllowed(
String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
- return getImplForUser().bindAppWidgetIdIfAllowed(packageName, appWidgetId, provider);
+ return getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetIdIfAllowed(
+ packageName, appWidgetId, provider);
}
@Override
public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
- return getImplForUser().hasBindAppWidgetPermission(packageName);
+ return getImplForUser(UserHandle.getCallingUserId()).hasBindAppWidgetPermission(
+ packageName);
}
@Override
public void setBindAppWidgetPermission(String packageName, boolean permission)
throws RemoteException {
- getImplForUser().setBindAppWidgetPermission(packageName, permission);
+ getImplForUser(UserHandle.getCallingUserId()).setBindAppWidgetPermission(
+ packageName, permission);
}
@Override
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
throws RemoteException {
- getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
+ getImplForUser(UserHandle.getCallingUserId()).bindRemoteViewsService(
+ appWidgetId, intent, connection);
}
@Override
public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
List<RemoteViews> updatedViews) throws RemoteException {
- return getImplForUser().startListening(host, packageName, hostId, updatedViews);
+ return getImplForUser(UserHandle.getCallingUserId()).startListening(host,
+ packageName, hostId, updatedViews);
}
public void onUserRemoved(int userId) {
@@ -247,8 +264,10 @@
}
}
- private AppWidgetServiceImpl getImplForUser() {
- final int userId = Binder.getOrigCallingUser();
+ public void onUserStopped(int userId) {
+ }
+
+ private AppWidgetServiceImpl getImplForUser(int userId) {
AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
if (service == null) {
Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
@@ -265,27 +284,27 @@
@Override
public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
- return getImplForUser().getAppWidgetIds(provider);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetIds(provider);
}
@Override
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetInfo(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetInfo(appWidgetId);
}
@Override
public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetViews(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetViews(appWidgetId);
}
@Override
public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
- getImplForUser().updateAppWidgetOptions(appWidgetId, options);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetOptions(appWidgetId, options);
}
@Override
public Bundle getAppWidgetOptions(int appWidgetId) {
- return getImplForUser().getAppWidgetOptions(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetOptions(appWidgetId);
}
static int[] getAppWidgetIds(Provider p) {
@@ -299,40 +318,43 @@
@Override
public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
- return getImplForUser().getInstalledProviders();
+ return getImplForUser(UserHandle.getCallingUserId()).getInstalledProviders();
}
@Override
public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
throws RemoteException {
- getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
+ getImplForUser(UserHandle.getCallingUserId()).notifyAppWidgetViewDataChanged(
+ appWidgetIds, viewId);
}
@Override
public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
throws RemoteException {
- getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).partiallyUpdateAppWidgetIds(
+ appWidgetIds, views);
}
@Override
public void stopListening(int hostId) throws RemoteException {
- getImplForUser().stopListening(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).stopListening(hostId);
}
@Override
public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
- getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
+ getImplForUser(UserHandle.getCallingUserId()).unbindRemoteViewsService(
+ appWidgetId, intent);
}
@Override
public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
- getImplForUser().updateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetIds(appWidgetIds, views);
}
@Override
public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
throws RemoteException {
- getImplForUser().updateAppWidgetProvider(provider, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetProvider(provider, views);
}
@Override
@@ -349,16 +371,29 @@
String action = intent.getAction();
// Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- getImplForUser().sendInitialBroadcasts();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId >= 0) {
+ getImplForUser(userId).sendInitialBroadcasts();
+ } else {
+ Slog.w(TAG, "Not user handle supplied in " + intent);
+ }
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
for (int i = 0; i < mAppWidgetServices.size(); i++) {
AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
service.onConfigurationChanged();
}
} else {
- for (int i = 0; i < mAppWidgetServices.size(); i++) {
- AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
- service.onBroadcastReceived(intent);
+ int sendingUser = getSendingUserId();
+ if (sendingUser == UserHandle.USER_ALL) {
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onBroadcastReceived(intent);
+ }
+ } else {
+ AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
+ if (service != null) {
+ service.onBroadcastReceived(intent);
+ }
}
}
}
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 539e561..57ab921 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -40,6 +40,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -1514,11 +1515,12 @@
String pkg = parser.getAttributeValue(null, "pkg");
String cl = parser.getAttributeValue(null, "cl");
- final PackageManager packageManager = mContext.getPackageManager();
+ final IPackageManager packageManager = AppGlobals.getPackageManager();
try {
- packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
- } catch (PackageManager.NameNotFoundException e) {
- String[] pkgs = packageManager
+ packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0,
+ UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ String[] pkgs = mContext.getPackageManager()
.currentToCanonicalPackageNames(new String[] { pkg });
pkg = pkgs[0];
}
@@ -1633,11 +1635,11 @@
}
static File getSettingsFile(int userId) {
- return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
+ return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
}
AtomicFile savedStateFile() {
- File dir = new File("/data/system/users/" + mUserId);
+ File dir = Environment.getUserSystemDirectory(mUserId);
File settingsFile = getSettingsFile(mUserId);
if (!settingsFile.exists() && mUserId == 0) {
if (!dir.exists()) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 8be0ba8..955ea23 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1663,8 +1663,7 @@
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer,
- Binder.getOrigCallingUser());
+ mActivityManager.clearApplicationUserData(packageName, observer, 0);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 4192a93..fe8529b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -33,6 +33,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -115,6 +116,7 @@
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
+ private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -137,6 +139,8 @@
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+ mShutdownBatteryTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shutdownBatteryTemperature);
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
@@ -227,9 +231,11 @@
}
private final void shutdownIfOverTemp() {
- // shut down gracefully if temperature is too high (> 68.0C)
- // wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
+ // shut down gracefully if temperature is too high (> 68.0C by default)
+ // wait until the system has booted before attempting to display the
+ // shutdown dialog.
+ if (mBatteryTemperature > mShutdownBatteryTemperature
+ && ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -258,7 +264,7 @@
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
-
+
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -267,7 +273,7 @@
} catch (RemoteException e) {
// Should never happen.
}
-
+
shutdownIfNoPower();
shutdownIfOverTemp();
@@ -345,21 +351,21 @@
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (mPlugType != 0 && mLastPlugType == 0) {
statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
else if (mPlugType == 0 && mLastPlugType != 0) {
statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
if (sendBatteryLow) {
mSentLowBatteryBroadcast = true;
statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
- mContext.sendBroadcast(statusIntent);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
// Update the battery LED
@@ -414,7 +420,7 @@
" icon:" + icon + " invalid charger:" + mInvalidCharger);
}
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
}
private final void logBatteryStats() {
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 4c98ac3..9404dce 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -23,6 +23,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.Binder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import java.util.List;
@@ -39,6 +40,8 @@
private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
+ //Maximum msec to wait for service restart
+ private static final int SERVICE_RESTART_TIME_MS = 200;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
@@ -48,6 +51,7 @@
private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
+ private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
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;
@@ -650,7 +654,8 @@
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
- mContext.sendBroadcast(intent,BLUETOOTH_PERM);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ BLUETOOTH_PERM);
}
break;
}
@@ -658,8 +663,36 @@
{
if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
sendBluetoothServiceDownCallback();
+
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ Message stateChangeMsg = mHandler.obtainMessage(
+ MESSAGE_BLUETOOTH_STATE_CHANGE);
+ stateChangeMsg.arg1 = BluetoothAdapter.STATE_ON;
+ stateChangeMsg.arg2 =
+ BluetoothAdapter.STATE_TURNING_OFF;
+ mHandler.sendMessage(stateChangeMsg);
+ synchronized(mConnection) {
+ mBluetooth = null;
+ }
+ // Send a Bluetooth Restart message
+ Message restartMsg = mHandler.obtainMessage(
+ MESSAGE_RESTART_BLUETOOTH_SERVICE);
+ mHandler.sendMessageDelayed(restartMsg,
+ SERVICE_RESTART_TIME_MS);
break;
}
+ case MESSAGE_RESTART_BLUETOOTH_SERVICE:
+ {
+ Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
+ +" Restart IBluetooth service");
+ /* Enable without persisting the setting as
+ it doesnt change when IBluetooth
+ service restarts */
+ handleEnable(false, mQuietEnable);
+ break;
+ }
+
case MESSAGE_TIMEOUT_UNBIND:
{
Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 776a1a4..3a338a9 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -42,6 +42,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.CaptivePortalTracker;
import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
@@ -76,10 +77,12 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
@@ -166,6 +169,9 @@
*/
private NetworkStateTracker mNetTrackers[];
+ /* Handles captive portal check on a network */
+ private CaptivePortalTracker mCaptivePortalTracker;
+
/**
* The link properties that define the current links
*/
@@ -1363,8 +1369,10 @@
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
+ DetailedState netState = tracker.getNetworkInfo().getDetailedState();
- if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
+ if (tracker == null || (netState != DetailedState.CONNECTED &&
+ netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
tracker.isTeardownRequested()) {
if (VDBG) {
log("requestRouteToHostAddress on down network " +
@@ -1845,7 +1853,13 @@
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
- mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+ RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -1919,7 +1933,12 @@
log("sendStickyBroadcast: action=" + intent.getAction());
}
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -1940,7 +1959,7 @@
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
- mContext.sendStickyBroadcast(mInitialBroadcast);
+ mContext.sendStickyBroadcastAsUser(mInitialBroadcast, UserHandle.ALL);
mInitialBroadcast = null;
}
}
@@ -1966,32 +1985,29 @@
}
};
- private void handleConnect(NetworkInfo info) {
- final int type = info.getType();
+ private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
+ if ((type != mNetworkPreference &&
+ mNetConfigs[mActiveDefaultNetwork].priority >
+ mNetConfigs[type].priority) ||
+ mNetworkPreference == mActiveDefaultNetwork) return false;
+ return true;
+ }
- setupDataActivityTracking(type);
+ private void handleConnect(NetworkInfo info) {
+ final int newNetType = info.getType();
+
+ setupDataActivityTracking(newNetType);
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
- final NetworkStateTracker thisNet = mNetTrackers[type];
+ final NetworkStateTracker thisNet = mNetTrackers[newNetType];
final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
- if (mNetConfigs[type].isDefault()) {
- if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
- if ((type != mNetworkPreference &&
- mNetConfigs[mActiveDefaultNetwork].priority >
- mNetConfigs[type].priority) ||
- mNetworkPreference == mActiveDefaultNetwork) {
- // don't accept this one
- if (VDBG) {
- log("Not broadcasting CONNECT_ACTION " +
- "to torn down network " + info.getTypeName());
- }
- teardown(thisNet);
- return;
- } else {
+ if (mNetConfigs[newNetType].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
+ if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork];
@@ -2004,6 +2020,14 @@
teardown(thisNet);
return;
}
+ } else {
+ // don't accept this one
+ if (VDBG) {
+ log("Not broadcasting CONNECT_ACTION " +
+ "to torn down network " + info.getTypeName());
+ }
+ teardown(thisNet);
+ return;
}
}
synchronized (ConnectivityService.this) {
@@ -2017,7 +2041,7 @@
1000);
}
}
- mActiveDefaultNetwork = type;
+ mActiveDefaultNetwork = newNetType;
// this will cause us to come up initially as unconnected and switching
// to connected after our normal pause unless somebody reports us as reall
// disconnected
@@ -2029,19 +2053,47 @@
}
thisNet.setTeardownRequested(false);
updateNetworkSettings(thisNet);
- handleConnectivityChange(type, false);
+ handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
if (thisIface != null) {
try {
- BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type);
+ BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, newNetType);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
}
+ private void handleCaptivePortalTrackerCheck(NetworkInfo info) {
+ if (DBG) log("Captive portal check " + info);
+ int type = info.getType();
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ if (mNetConfigs[type].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+ if (isNewNetTypePreferredOverCurrentNetType(type)) {
+ if (DBG) log("Captive check on " + info.getTypeName());
+ mCaptivePortalTracker = CaptivePortalTracker.detect(mContext, info,
+ ConnectivityService.this);
+ return;
+ } else {
+ if (DBG) log("Tear down low priority net " + info.getTypeName());
+ teardown(thisNet);
+ return;
+ }
+ }
+ }
+
+ thisNet.captivePortalCheckComplete();
+ }
+
+ /** @hide */
+ public void captivePortalCheckComplete(NetworkInfo info) {
+ mNetTrackers[info.getType()].captivePortalCheckComplete();
+ mCaptivePortalTracker = null;
+ }
+
/**
* Setup data activity tracking for the given network interface.
*
@@ -2426,7 +2478,12 @@
* Connectivity events can happen before boot has completed ...
*/
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
// Caller must grab mDnsLock.
@@ -2630,6 +2687,9 @@
if (info.getDetailedState() ==
NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
+ } else if (info.getDetailedState() ==
+ DetailedState.CAPTIVE_PORTAL_CHECK) {
+ handleCaptivePortalTrackerCheck(info);
} else if (state == NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (state == NetworkInfo.State.SUSPENDED) {
@@ -3068,7 +3128,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private static class SettingsObserver extends ContentObserver {
@@ -3306,7 +3371,7 @@
@Override
public boolean updateLockdownVpn() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
// Tear down existing lockdown if profile was removed
mLockdownEnabled = LockdownVpnTracker.isEnabled();
@@ -3357,4 +3422,11 @@
throw new IllegalStateException("Unavailable in lockdown mode");
}
}
+
+ private static void enforceSystemUid() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only available to AID_SYSTEM");
+ }
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index f966a33..61517b1 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -55,6 +55,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -176,6 +177,9 @@
static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+ static final int DEF_KEYGUARD_WIDGET_DISABLED = 0; // none
+ int disableKeyguardWidgets = DEF_KEYGUARD_WIDGET_DISABLED;
+
boolean encryptionRequested = false;
boolean disableCamera = false;
@@ -285,6 +289,11 @@
out.attribute(null, "value", Boolean.toString(disableCamera));
out.endTag(null, "disable-camera");
}
+ if (disableKeyguardWidgets != DEF_KEYGUARD_WIDGET_DISABLED) {
+ out.startTag(null, "disable-keyguard-widgets");
+ out.attribute(null, "value", Integer.toString(disableKeyguardWidgets));
+ out.endTag(null, "disable-keyguard-widgets");
+ }
}
void readFromXml(XmlPullParser parser)
@@ -570,10 +579,10 @@
intent.putExtra("expiration", admin.passwordExpirationDate);
}
if (result != null) {
- mContext.sendOrderedBroadcast(intent, null, result, mHandler,
- Activity.RESULT_OK, null, null);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ null, result, mHandler, Activity.RESULT_OK, null, null);
} else {
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
}
}
@@ -712,7 +721,7 @@
private void sendChangedNotification() {
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void loadSettingsLocked() {
@@ -1734,7 +1743,8 @@
}
Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
intent.setComponent(admin.info.getComponent());
- mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
+ null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
try {
@@ -2091,6 +2101,46 @@
}
}
+ /**
+ * Selectively disable keyguard widgets.
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName who, int which) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_WIDGETS);
+ if ((ap.disableKeyguardWidgets & which) != which) {
+ ap.disableKeyguardWidgets |= which;
+ saveSettingsLocked();
+ }
+ syncDeviceCapabilitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the disabled state for widgets in keyguard for the given admin,
+ * or the aggregate of all active admins if who is null.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName who) {
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return (admin != null) ? admin.disableKeyguardWidgets : 0;
+ }
+
+ // Determine whether or not keyguard widgets are disabled for any active admins.
+ final int N = mAdminList.size();
+ int which = 0;
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ which |= admin.disableKeyguardWidgets;
+ }
+ return which;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 9231674..c919595 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -39,6 +39,7 @@
import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Formatter;
import android.util.EventLog;
@@ -416,8 +417,8 @@
//cancel notification since memory has been freed
mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID);
- mContext.removeStickyBroadcast(mStorageLowIntent);
- mContext.sendBroadcast(mStorageOkIntent);
+ mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL);
}
/**
@@ -433,8 +434,8 @@
*/
private final void cancelFullNotification() {
if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
- mContext.removeStickyBroadcast(mStorageFullIntent);
- mContext.sendBroadcast(mStorageNotFullIntent);
+ mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
}
public void updateMemory() {
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index f1ff27f..65c39f2 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -34,6 +34,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -226,7 +227,7 @@
}
} else {
// dreams feature not enabled, send legacy intent
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
}
}
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 932cba1..0b12410 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -31,6 +31,7 @@
import android.os.Message;
import android.os.StatFs;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
import android.util.Slog;
@@ -157,7 +158,8 @@
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SEND_BROADCAST) {
- mContext.sendBroadcast((Intent)msg.obj, android.Manifest.permission.READ_LOGS);
+ mContext.sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
+ android.Manifest.permission.READ_LOGS);
}
}
};
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index dd50beb..39355d5 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -148,3 +148,11 @@
# ---------------------------
51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
+
+
+# ---------------------------
+# LockdownVpnTracker.java
+# ---------------------------
+51200 lockdown_vpn_connecting (egress_net|1)
+51201 lockdown_vpn_connected (egress_net|1)
+51202 lockdown_vpn_error (egress_net|1)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 747cf0b..c685473 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -3579,7 +3579,7 @@
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mSubtypesMap =
+ private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
new HashMap<String, List<InputMethodSubtype>>();
public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap) {
if (methodMap == null) {
@@ -3595,18 +3595,19 @@
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile);
if (!subtypeFile.exists()) {
// If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- methodMap);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
} else {
- readAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile);
+ readAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
}
}
private void deleteAllInputMethodSubtypes(String imiId) {
synchronized (mMethodMap) {
- mSubtypesMap.remove(imiId);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.remove(imiId);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
@@ -3619,17 +3620,20 @@
final InputMethodSubtype subtype = additionalSubtypes[i];
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
- mSubtypesMap.put(imi.getId(), subtypes);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.put(imi.getId(), subtypes);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
- return mSubtypesMap;
+ return mAdditionalSubtypesMap;
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3a45720..37dae35 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
@@ -51,6 +52,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.NameValueTable;
@@ -86,7 +88,7 @@
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
-public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Runnable {
private static final String TAG = "LocationManagerService";
public static final boolean D = false;
@@ -206,24 +208,30 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mPackageManager = mContext.getPackageManager();
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mLocationFudger = new LocationFudger();
+
synchronized (mLock) {
loadProvidersLocked();
}
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
+
mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- mLocationFudger = new LocationFudger();
// listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + NameValueTable.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
- ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
- mLocationHandler);
- settingsCursor.close();
- query.addObserver(this);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateProvidersLocked();
+ }
+ }
+ });
mPackageMonitor.register(mContext, Looper.myLooper(), true);
+
+ updateProvidersLocked();
}
private void loadProvidersLocked() {
@@ -298,8 +306,6 @@
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
-
- updateProvidersLocked();
}
/**
@@ -543,14 +549,6 @@
}
}
- /** Settings Observer callback */
- @Override
- public void update(Observable o, Object arg) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
- }
-
private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
@@ -719,7 +717,8 @@
}
}
if (changesMade) {
- mContext.sendBroadcast(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
+ mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+ UserHandle.ALL);
}
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index bb5d552..f40333d 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1047,12 +1047,13 @@
// add StorageVolume extra
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
Slog.d(TAG, "sendStorageIntent " + intent);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendUmsIntent(boolean c) {
- mContext.sendBroadcast(
- new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
+ mContext.sendBroadcastAsUser(
+ new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
+ UserHandle.ALL);
}
private void validatePermission(String perm) {
@@ -1113,9 +1114,8 @@
Slog.e(TAG, "path or description is null in readStorageList");
} else {
String pathString = path.toString();
- StorageVolume volume = new StorageVolume(pathString,
- descriptionId, removable, emulated,
- mtpReserve, allowMassStorage, maxFileSize);
+ StorageVolume volume = new StorageVolume(pathString, descriptionId, primary,
+ removable, emulated, mtpReserve, allowMassStorage, maxFileSize);
if (primary) {
if (mPrimaryVolume == null) {
mPrimaryVolume = volume;
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index efa16af..3ddae3e 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -45,8 +45,10 @@
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -1436,7 +1438,7 @@
@Override
public void setFirewallEnabled(boolean enabled) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
try {
mConnector.execute("firewall", enabled ? "enable" : "disable");
mFirewallEnabled = enabled;
@@ -1447,13 +1449,13 @@
@Override
public boolean isFirewallEnabled() {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
return mFirewallEnabled;
}
@Override
public void setFirewallInterfaceRule(String iface, boolean allow) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
final String rule = allow ? ALLOW : DENY;
try {
@@ -1465,7 +1467,7 @@
@Override
public void setFirewallEgressSourceRule(String addr, boolean allow) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
final String rule = allow ? ALLOW : DENY;
try {
@@ -1477,7 +1479,7 @@
@Override
public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
final String rule = allow ? ALLOW : DENY;
try {
@@ -1489,7 +1491,7 @@
@Override
public void setFirewallUidRule(int uid, boolean allow) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
final String rule = allow ? ALLOW : DENY;
try {
@@ -1499,6 +1501,13 @@
}
}
+ private static void enforceSystemUid() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only available to AID_SYSTEM");
+ }
+ }
+
@Override
public void monitor() {
if (mConnector != null) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index d6fed39..3caba1f 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -20,7 +20,9 @@
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
@@ -149,8 +151,6 @@
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
- private IDreamManager mSandman;
-
private static final int DB_VERSION = 1;
private static final String TAG_BODY = "notification-policy";
@@ -317,17 +317,20 @@
final int id;
final int uid;
final int initialPid;
+ final int userId;
final Notification notification;
final int score;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+ int userId, int score, Notification notification)
{
this.pkg = pkg;
this.tag = tag;
this.id = id;
this.uid = uid;
this.initialPid = initialPid;
+ this.userId = userId;
this.score = score;
this.notification = notification;
}
@@ -342,7 +345,7 @@
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
pw.println(prefix + " contentView=" + notification.contentView);
- pw.println(prefix + " uid=" + uid);
+ pw.println(prefix + " uid=" + uid + " userId=" + userId);
pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + " sound=" + notification.sound);
@@ -429,18 +432,25 @@
}
public void onClearAll() {
- cancelAll();
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelAll(ActivityManager.getCurrentUser());
}
public void onNotificationClick(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE, false);
+ Notification.FLAG_FOREGROUND_SERVICE, false,
+ ActivityManager.getCurrentUser());
}
public void onNotificationClear(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
- true);
+ true, ActivityManager.getCurrentUser());
}
public void onPanelRevealed() {
@@ -479,7 +489,9 @@
int uid, int initialPid, String message) {
Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
- cancelNotification(pkg, tag, id, 0, 0, false);
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
long ident = Binder.clearCallingIdentity();
try {
ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -531,7 +543,8 @@
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
- cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
+ cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
+ UserHandle.USER_ALL);
}
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
@@ -544,6 +557,11 @@
mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK));
updateNotificationPulse();
+ } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0) {
+ cancelAllNotificationsInt(null, 0, 0, true, userHandle);
+ }
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
@@ -619,6 +637,7 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -637,8 +656,6 @@
void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
- mSandman = IDreamManager.Stub.asInterface(
- ServiceManager.getService("dreams"));
// no beeping until we're basically done booting
mSystemReady = true;
@@ -849,17 +866,11 @@
// Notifications
// ============================================================================
- @Deprecated
- public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
- {
- enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
- }
-
public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- int[] idOut)
+ int[] idOut, int userId)
{
enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
- tag, id, notification, idOut);
+ tag, id, notification, idOut, userId);
}
private final static int clamp(int x, int low, int high) {
@@ -870,7 +881,7 @@
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
// uid/pid of another application)
public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- String tag, int id, Notification notification, int[] idOut)
+ String tag, int id, Notification notification, int[] idOut, int userId)
{
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
@@ -878,6 +889,9 @@
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = ("android".equals(pkg));
+ userId = ActivityManager.handleIncomingUser(callingPid,
+ callingUid, userId, false, true, "enqueueNotification", pkg);
+
// Limit the number of notifications that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification) {
@@ -952,12 +966,12 @@
synchronized (mNotificationList) {
NotificationRecord r = new NotificationRecord(pkg, tag, id,
- callingUid, callingPid,
+ callingUid, callingPid, userId,
score,
notification);
NotificationRecord old = null;
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
} else {
@@ -977,16 +991,6 @@
| Notification.FLAG_NO_CLEAR;
}
- // Stop screensaver if the notification has a full-screen intent.
- // (like an incoming phone call)
- if (notification.fullScreenIntent != null && mSandman != null) {
- try {
- mSandman.awaken();
- } catch (RemoteException e) {
- // noop
- }
- }
-
if (notification.icon != 0) {
StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
r.uid, r.initialPid, score, notification);
@@ -1029,6 +1033,7 @@
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (r.userId == UserHandle.USER_ALL || r.userId == userId)
&& mSystemReady) {
final AudioManager audioManager = (AudioManager) mContext
@@ -1188,12 +1193,12 @@
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
- int mustNotHaveFlags, boolean sendDelete) {
+ int mustNotHaveFlags, boolean sendDelete, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag,
mustHaveFlags, mustNotHaveFlags);
synchronized (mNotificationList) {
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
@@ -1217,7 +1222,7 @@
* {@code mustHaveFlags}.
*/
boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
- int mustNotHaveFlags, boolean doit) {
+ int mustNotHaveFlags, boolean doit, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags,
mustNotHaveFlags);
@@ -1226,6 +1231,9 @@
boolean canceledSomething = false;
for (int i = N-1; i >= 0; --i) {
NotificationRecord r = mNotificationList.get(i);
+ if (userId != UserHandle.USER_ALL && r.userId != userId) {
+ continue;
+ }
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
continue;
}
@@ -1249,25 +1257,25 @@
}
}
- @Deprecated
- public void cancelNotification(String pkg, int id) {
- cancelNotificationWithTag(pkg, null /* tag */, id);
- }
-
- public void cancelNotificationWithTag(String pkg, String tag, int id) {
+ public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "cancelNotificationWithTag", pkg);
// Don't allow client applications to cancel foreground service notis.
cancelNotification(pkg, tag, id, 0,
Binder.getCallingUid() == Process.SYSTEM_UID
- ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
}
- public void cancelAllNotifications(String pkg) {
+ public void cancelAllNotifications(String pkg, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, true, "cancelAllNotifications", pkg);
+
// Calling from user space, don't allow the canceling of actively
// running foreground services.
- cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
+ cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
}
void checkCallerIsSystem() {
@@ -1284,23 +1292,27 @@
return;
}
try {
- ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
- pkg, 0);
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ pkg, 0, UserHandle.getCallingUserId());
if (!UserHandle.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
- } catch (PackageManager.NameNotFoundException e) {
- throw new SecurityException("Unknown package " + pkg);
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown package " + pkg + "\n" + re);
}
}
- void cancelAll() {
+ void cancelAll(int userId) {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
+ if (r.userId != userId) {
+ continue;
+ }
+
if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
@@ -1345,12 +1357,15 @@
}
// lock on mNotificationList
- private int indexOfNotificationLocked(String pkg, String tag, int id)
+ private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
ArrayList<NotificationRecord> list = mNotificationList;
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
+ if (r.userId != userId || r.id != id) {
+ continue;
+ }
if (tag == null) {
if (r.tag != null) {
continue;
@@ -1360,7 +1375,7 @@
continue;
}
}
- if (r.id == id && r.pkg.equals(pkg)) {
+ if (r.pkg.equals(pkg)) {
return i;
}
}
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
index 87843d9..2a7a2eb 100644
--- a/services/java/com/android/server/NsdService.java
+++ b/services/java/com/android/server/NsdService.java
@@ -31,6 +31,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.IBinder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -448,7 +449,7 @@
} else {
intent.putExtra(NsdManager.EXTRA_NSD_STATE, NsdManager.NSD_STATE_DISABLED);
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private boolean isNsdEnabled() {
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 78c0c12..9f53fad 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -27,7 +27,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.View;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index a7a583c..8cf273d 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -24,8 +24,10 @@
import android.app.backup.FullBackupDataOutput;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Slog;
@@ -45,11 +47,13 @@
private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_IMAGE_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_IMAGE_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_INFO_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_INFO_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7097891..1aad9b3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,8 @@
import android.content.res.Configuration;
import android.media.AudioService;
import android.net.wifi.p2p.WifiP2pService;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SchedulingPolicyService;
@@ -147,7 +149,52 @@
CommonTimeManagementService commonTimeMgmtService = null;
InputManagerService inputManager = null;
+ // Create a shared handler thread for UI within the system server.
+ // This thread is used by at least the following components:
+ // - WindowManagerPolicy
+ // - KeyguardViewManager
+ // - DisplayManagerService
+ HandlerThread uiHandlerThread = new HandlerThread("UI");
+ uiHandlerThread.start();
+ Handler uiHandler = new Handler(uiHandlerThread.getLooper());
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
+ // Create a handler thread just for the window manager to enjoy.
+ HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
+ wmHandlerThread.start();
+ Handler wmHandler = new Handler(wmHandlerThread.getLooper());
+ wmHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
// Critical services...
+ boolean onlyCore = false;
try {
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer());
@@ -160,7 +207,7 @@
context = ActivityManagerService.main(factoryTest);
Slog.i(TAG, "Display Manager");
- display = new DisplayManagerService(context);
+ display = new DisplayManagerService(context, wmHandler, uiHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
Slog.i(TAG, "Telephony Registry");
@@ -172,10 +219,14 @@
AttributeCache.init(context);
+ if (!display.waitForDefaultDisplay()) {
+ reportWtf("Timeout waiting for default display to be initialized.",
+ new Throwable());
+ }
+
Slog.i(TAG, "Package Manager");
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
- boolean onlyCore = false;
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
onlyCore = true;
@@ -244,6 +295,7 @@
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display,
+ uiHandler, wmHandler,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -251,6 +303,7 @@
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
+ display.setWindowManager(wm);
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
@@ -753,6 +806,12 @@
reportWtf("making Package Manager Service ready", e);
}
+ try {
+ display.systemReady(safeMode, onlyCore);
+ } catch (Throwable e) {
+ reportWtf("making Display Manager Service ready", e);
+ }
+
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 087e8db..8361477 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -588,7 +589,7 @@
Bundle data = new Bundle();
state.fillInNotifierBundle(data);
intent.putExtras(data);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength) {
@@ -606,7 +607,7 @@
Bundle data = new Bundle();
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastCallStateChanged(int state, String incomingNumber) {
@@ -629,7 +630,8 @@
if (!TextUtils.isEmpty(incomingNumber)) {
intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
}
- mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.READ_PHONE_STATE);
}
private void broadcastDataConnectionStateChanged(int state,
@@ -662,14 +664,14 @@
intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void broadcastDataConnectionFailed(String reason, String apnType) {
Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private boolean checkNotifyPermission(String method) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 98e6dc0..49f39fe 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -44,6 +44,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -368,7 +369,7 @@
}
if (mPollStickyBroadcast != null) {
- mContext.removeStickyBroadcast(mPollStickyBroadcast);
+ mContext.removeStickyBroadcastAsUser(mPollStickyBroadcast, UserHandle.ALL);
}
}
@@ -494,7 +495,7 @@
onPollAlarm();
Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
- mContext.sendBroadcast(broadcast);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
}
private void onPollAlarm() {
@@ -563,7 +564,7 @@
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
mPollStickyBroadcast = broadcast;
mAlarmManager.cancel(mPendingPollIntent);
@@ -621,7 +622,7 @@
Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
mPolicyThrottleValue.get());
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
} // else already up!
} else {
@@ -699,7 +700,7 @@
}
Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
mNotificationManager.cancel(R.drawable.stat_sys_throttled);
mWarningNotificationSent = false;
}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 617b29d..f618263 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -37,6 +37,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -164,7 +165,7 @@
try {
ActivityManagerNative.getDefault().startActivityWithConfig(
null, homeIntent, null, null, null, 0, 0,
- newConfig, null);
+ newConfig, null, UserHandle.USER_CURRENT);
mHoldingConfiguration = false;
} catch (RemoteException e) {
Slog.w(TAG, e.getCause());
@@ -397,7 +398,7 @@
adjustStatusBarCarModeLocked();
if (oldAction != null) {
- mContext.sendBroadcast(new Intent(oldAction));
+ mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
}
mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
action = UiModeManager.ACTION_ENTER_CAR_MODE;
@@ -405,7 +406,7 @@
} else if (isDeskDockState(mDockState)) {
if (!isDeskDockState(mLastBroadcastState)) {
if (oldAction != null) {
- mContext.sendBroadcast(new Intent(oldAction));
+ mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL);
}
mLastBroadcastState = mDockState;
action = UiModeManager.ACTION_ENTER_DESK_MODE;
@@ -431,7 +432,7 @@
Intent intent = new Intent(action);
intent.putExtra("enableFlags", enableFlags);
intent.putExtra("disableFlags", disableFlags);
- mContext.sendOrderedBroadcast(intent, null,
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mResultReceiver, null, Activity.RESULT_OK, null, null);
// Attempting to make this transition a little more clean, we are going
// to hold off on doing a configuration change until we have finished
diff --git a/services/java/com/android/server/UpdateLockService.java b/services/java/com/android/server/UpdateLockService.java
index 1ffd196..0f778cd 100644
--- a/services/java/com/android/server/UpdateLockService.java
+++ b/services/java/com/android/server/UpdateLockService.java
@@ -27,6 +27,7 @@
import android.os.SystemClock;
import android.os.TokenWatcher;
import android.os.UpdateLock;
+import android.os.UserHandle;
import android.util.Slog;
import java.io.FileDescriptor;
@@ -78,7 +79,7 @@
.putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
.putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally {
Binder.restoreCallingIdentity(oldIdent);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index afd7d0e..a807f4c 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -19,6 +19,7 @@
import static android.os.FileObserver.*;
import static android.os.ParcelFileDescriptor.*;
+import android.app.AppGlobals;
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
@@ -31,6 +32,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -90,8 +92,6 @@
* restarting it vs. just reverting to the static wallpaper.
*/
static final long MIN_WALLPAPER_CRASH_TIME = 10000;
-
- static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
@@ -146,6 +146,7 @@
final Context mContext;
final IWindowManager mIWindowManager;
+ final IPackageManager mIPackageManager;
final MyPackageMonitor mMonitor;
WallpaperData mLastWallpaper;
@@ -389,14 +390,15 @@
mContext = context;
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mIPackageManager = AppGlobals.getPackageManager();
mMonitor = new MyPackageMonitor();
mMonitor.register(context, null, true);
- WALLPAPER_BASE_DIR.mkdirs();
- loadSettingsLocked(0);
+ getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
+ loadSettingsLocked(UserHandle.USER_OWNER);
}
private static File getWallpaperDir(int userId) {
- return new File(WALLPAPER_BASE_DIR + "/" + userId);
+ return Environment.getUserSystemDirectory(userId);
}
@Override
@@ -410,7 +412,7 @@
public void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
- WallpaperData wallpaper = mWallpaperMap.get(0);
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
@@ -710,8 +712,9 @@
if (DEBUG) Slog.v(TAG, "Using image wallpaper");
}
}
- ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
- PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
+ int serviceUserId = wallpaper.userId;
+ ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
String msg = "Selected service does not require "
+ android.Manifest.permission.BIND_WALLPAPER
@@ -728,8 +731,10 @@
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if (componentName != null && !componentName.equals(wallpaper.imageWallpaperComponent)) {
// Make sure the selected service is actually a wallpaper service.
- List<ResolveInfo> ris = mContext.getPackageManager()
- .queryIntentServices(intent, PackageManager.GET_META_DATA);
+ List<ResolveInfo> ris =
+ mIPackageManager.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.GET_META_DATA, serviceUserId);
for (int i=0; i<ris.size(); i++) {
ServiceInfo rsi = ris.get(i).serviceInfo;
if (rsi.name.equals(si.name) &&
@@ -767,14 +772,13 @@
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
intent.setComponent(componentName);
- int serviceUserId = wallpaper.userId;
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
mContext, 0,
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
- 0));
+ 0, null, new UserHandle(serviceUserId)));
if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
String msg = "Unable to bind service: "
+ componentName;
@@ -800,8 +804,8 @@
}
} catch (RemoteException e) {
}
- } catch (PackageManager.NameNotFoundException e) {
- String msg = "Unknown component " + componentName;
+ } catch (RemoteException e) {
+ String msg = "Remote exception for " + componentName + "\n" + e;
if (fromUser) {
throw new IllegalArgumentException(msg);
}
@@ -863,7 +867,7 @@
}
wallpaper.callbacks.finishBroadcast();
final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
private void checkPermission(String permission) {
@@ -874,7 +878,7 @@
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
+ final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index f483576..6bc5e10 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -240,7 +240,7 @@
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- Slog.d(TAG, "New client listening to asynchronous messages");
+ if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
mClients.add((AsyncChannel) msg.obj);
} else {
Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
@@ -249,9 +249,9 @@
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
- Slog.d(TAG, "Send failed, client connection lost");
+ if (DBG) Slog.d(TAG, "Send failed, client connection lost");
} else {
- Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
}
mClients.remove((AsyncChannel) msg.obj);
break;
@@ -412,6 +412,7 @@
switch(mNetworkInfo.getDetailedState()) {
case CONNECTED:
case DISCONNECTED:
+ case CAPTIVE_PORTAL_CHECK:
evaluateTrafficStatsPolling();
resetNotification();
break;
@@ -606,6 +607,12 @@
"WifiService");
}
+ private void enforceConnectivityInternalPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ "ConnectivityService");
+ }
+
/**
* see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
* @param enable {@code true} to enable, {@code false} to disable.
@@ -910,7 +917,7 @@
*
*/
public void startWifi() {
- enforceChangePermission();
+ enforceConnectivityInternalPermission();
/* TODO: may be add permissions for access only to connectivity service
* TODO: if a start issued, keep wifi alive until a stop issued irrespective
* of WifiLock & device idle status unless wifi enabled status is toggled
@@ -920,20 +927,24 @@
mWifiStateMachine.reconnectCommand();
}
+ public void captivePortalCheckComplete() {
+ enforceConnectivityInternalPermission();
+ mWifiStateMachine.captivePortalCheckComplete();
+ }
+
/**
* see {@link android.net.wifi.WifiManager#stopWifi}
*
*/
public void stopWifi() {
- enforceChangePermission();
- /* TODO: may be add permissions for access only to connectivity service
+ enforceConnectivityInternalPermission();
+ /*
* TODO: if a stop is issued, wifi is brought up only by startWifi
* unless wifi enabled status is toggled
*/
mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
}
-
/**
* see {@link android.net.wifi.WifiManager#addToBlacklist}
*
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
index fc774d4..f1a03de 100644
--- a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -26,57 +26,49 @@
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
-/**
- * Input filter for accessibility.
- *
- * Currently just a stub but will eventually implement touch exploration, etc.
- */
-public class AccessibilityInputFilter extends InputFilter {
- private static final String TAG = "AccessibilityInputFilter";
+class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
+
+ private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
+
private static final boolean DEBUG = false;
+ private static final int UNDEFINED_DEVICE_ID = -1;
+
+ /**
+ * Flag for enabling the screen magnification feature.
+ *
+ * @see #setEnabledFeatures(int)
+ */
+ static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
+
+ /**
+ * Flag for enabling the touch exploration feature.
+ *
+ * @see #setEnabledFeatures(int)
+ */
+ static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
+
private final Context mContext;
private final PowerManager mPm;
private final AccessibilityManagerService mAms;
- /**
- * This is an interface for explorers that take a {@link MotionEvent}
- * stream and perform touch exploration of the screen content.
- */
- public interface Explorer {
- /**
- * Handles a {@link MotionEvent}.
- *
- * @param event The event to handle.
- * @param policyFlags The policy flags associated with the event.
- */
- public void onMotionEvent(MotionEvent event, int policyFlags);
+ private int mCurrentDeviceId;
- /**
- * Requests that the explorer clears its internal state.
- *
- * @param event The last received event.
- * @param policyFlags The policy flags associated with the event.
- */
- public void clear(MotionEvent event, int policyFlags);
+ private boolean mInstalled;
- /**
- * Requests that the explorer clears its internal state.
- */
- public void clear();
- }
+ private int mEnabledFeatures;
private TouchExplorer mTouchExplorer;
+ private ScreenMagnifier mScreenMagnifier;
+ private EventStreamTransformation mEventHandler;
- private int mTouchscreenSourceDeviceId;
-
- public AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
+ AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
super(context.getMainLooper());
mContext = context;
mAms = service;
- mPm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
}
@Override
@@ -84,7 +76,9 @@
if (DEBUG) {
Slog.d(TAG, "Accessibility input filter installed.");
}
- mTouchExplorer = new TouchExplorer(this, mContext, mAms);
+ mInstalled = true;
+ disableFeatures();
+ enableFeatures();
super.onInstalled();
}
@@ -93,7 +87,8 @@
if (DEBUG) {
Slog.d(TAG, "Accessibility input filter uninstalled.");
}
- mTouchExplorer.clear();
+ mInstalled = false;
+ disableFeatures();
super.onUninstalled();
}
@@ -103,27 +98,104 @@
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
- if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
- MotionEvent motionEvent = (MotionEvent) event;
- int deviceId = event.getDeviceId();
- if (mTouchscreenSourceDeviceId != deviceId) {
- mTouchscreenSourceDeviceId = deviceId;
- mTouchExplorer.clear(motionEvent, policyFlags);
- }
- if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
- mPm.userActivity(event.getEventTime(), false);
- mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
- } else {
- mTouchExplorer.clear(motionEvent, policyFlags);
- }
- } else {
+ if (mEventHandler == null) {
super.onInputEvent(event, policyFlags);
+ return;
+ }
+ if (event.getSource() != InputDevice.SOURCE_TOUCHSCREEN) {
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
+ mEventHandler.clear();
+ super.onInputEvent(event, policyFlags);
+ return;
+ }
+ final int deviceId = event.getDeviceId();
+ if (mCurrentDeviceId != deviceId) {
+ if (mCurrentDeviceId != UNDEFINED_DEVICE_ID) {
+ mEventHandler.clear();
+ }
+ mCurrentDeviceId = deviceId;
+ }
+ mPm.userActivity(event.getEventTime(), false);
+ MotionEvent motionEvent = (MotionEvent) event;
+ mEventHandler.onMotionEvent(motionEvent, policyFlags);
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ // TODO Implement this to inject the accessibility event
+ // into the accessibility manager service similarly
+ // to how this is done for input events.
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation sink) {
+ /* do nothing */
+ }
+
+ @Override
+ public void clear() {
+ /* do nothing */
+ }
+
+ void setEnabledFeatures(int enabledFeatures) {
+ if (mEnabledFeatures == enabledFeatures) {
+ return;
+ }
+ if (mInstalled) {
+ disableFeatures();
+ }
+ mEnabledFeatures = enabledFeatures;
+ if (mInstalled) {
+ enableFeatures();
}
}
- public void onAccessibilityEvent(AccessibilityEvent event) {
- if (mTouchExplorer != null) {
- mTouchExplorer.onAccessibilityEvent(event);
+ void notifyAccessibilityEvent(AccessibilityEvent event) {
+ if (mEventHandler != null) {
+ mEventHandler.onAccessibilityEvent(event);
}
}
+
+ private void enableFeatures() {
+ if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+ mEventHandler = mScreenMagnifier = new ScreenMagnifier(mContext);
+ mEventHandler.setNext(this);
+ }
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ mTouchExplorer = new TouchExplorer(mContext, mAms);
+ mTouchExplorer.setNext(this);
+ if (mEventHandler != null) {
+ mEventHandler.setNext(mTouchExplorer);
+ } else {
+ mEventHandler = mTouchExplorer;
+ }
+ }
+ }
+
+ private void disableFeatures() {
+ if (mTouchExplorer != null) {
+ mTouchExplorer.clear();
+ mTouchExplorer.onDestroy();
+ mTouchExplorer = null;
+ }
+ if (mScreenMagnifier != null) {
+ mScreenMagnifier.clear();
+ mScreenMagnifier.onDestroy();
+ mScreenMagnifier = null;
+ }
+ mEventHandler = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ /* ignore */
+ }
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 857334e..f6354bb 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -48,6 +48,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -62,6 +63,7 @@
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
@@ -115,6 +117,8 @@
private static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 3;
+ private static final int MSG_SEND_UPDATE_INPUT_FILTER = 4;
+
private static int sIdCounter = 0;
private static int sNextWindowId;
@@ -157,11 +161,13 @@
private boolean mIsTouchExplorationEnabled;
+ private boolean mIsScreenMagnificationEnabled;
+
private final IWindowManager mWindowManager;
private final SecurityPolicy mSecurityPolicy;
- private final MainHanler mMainHandler;
+ private final MainHandler mMainHandler;
private Service mUiAutomationService;
@@ -183,7 +189,7 @@
mPackageManager = mContext.getPackageManager();
mWindowManager = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
mSecurityPolicy = new SecurityPolicy();
- mMainHandler = new MainHanler();
+ mMainHandler = new MainHandler(mContext.getMainLooper());
registerPackageChangeAndBootCompletedBroadcastReceiver();
registerSettingsContentObservers();
}
@@ -201,7 +207,7 @@
synchronized (mLock) {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
- populateAccessibilityServiceListLocked();
+ populateInstalledAccessibilityServiceLocked();
manageServicesLocked();
}
}
@@ -262,18 +268,11 @@
synchronized (mLock) {
// We will update when the automation service dies.
if (mUiAutomationService == null) {
- populateAccessibilityServiceListLocked();
- populateEnabledAccessibilityServicesLocked();
- populateTouchExplorationGrantedAccessibilityServicesLocked();
- handleAccessibilityEnabledSettingChangedLocked();
- handleTouchExplorationEnabledSettingChangedLocked();
- updateInputFilterLocked();
- sendStateToClientsLocked();
+ updateInternalStateLocked();
}
}
return;
}
-
super.onReceive(context, intent);
}
};
@@ -329,6 +328,24 @@
}
});
+ Uri accessibilityScreenMagnificationEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ contentResolver.registerContentObserver(accessibilityScreenMagnificationEnabledUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ handleScreenMagnificationEnabledSettingChangedLocked();
+ updateInputFilterLocked();
+ sendStateToClientsLocked();
+ }
+ }
+ }
+ });
+
Uri accessibilityServicesUri =
Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
contentResolver.registerContentObserver(accessibilityServicesUri, false,
@@ -587,8 +604,11 @@
final int windowId = mSecurityPolicy.mActiveWindowId;
IBinder token = mWindowIdToWindowTokenMap.get(windowId);
try {
- mWindowManager.getWindowFrame(token, outBounds);
- return true;
+ WindowInfo info = mWindowManager.getWindowInfo(token);
+ if (info != null) {
+ outBounds.set(info.frame);
+ return true;
+ }
} catch (RemoteException re) {
/* ignore */
}
@@ -652,7 +672,7 @@
/**
* Populates the cached list of installed {@link AccessibilityService}s.
*/
- private void populateAccessibilityServiceListLocked() {
+ private void populateInstalledAccessibilityServiceLocked() {
mInstalledServices.clear();
List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
@@ -962,31 +982,26 @@
}
/**
- * Updates the touch exploration state.
+ * Updates the state of the input filter.
*/
private void updateInputFilterLocked() {
- if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
- if (!mHasInputFilter) {
- mHasInputFilter = true;
- if (mInputFilter == null) {
- mInputFilter = new AccessibilityInputFilter(mContext, this);
- }
- try {
- mWindowManager.setInputFilter(mInputFilter);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- return;
- }
- if (mHasInputFilter) {
- mHasInputFilter = false;
- try {
- mWindowManager.setInputFilter(null);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
+ mMainHandler.obtainMessage(MSG_SEND_UPDATE_INPUT_FILTER).sendToTarget();
+ }
+
+ /**
+ * Updated the internal state of this service to match the current settings.
+ */
+ private void updateInternalStateLocked() {
+ populateInstalledAccessibilityServiceLocked();
+ populateEnabledAccessibilityServicesLocked();
+ populateTouchExplorationGrantedAccessibilityServicesLocked();
+
+ handleTouchExplorationEnabledSettingChangedLocked();
+ handleScreenMagnificationEnabledSettingChangedLocked();
+ handleAccessibilityEnabledSettingChangedLocked();
+
+ updateInputFilterLocked();
+ sendStateToClientsLocked();
}
/**
@@ -1012,6 +1027,15 @@
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
}
+ /**
+ * Updates the state based on the screen magnification enabled setting.
+ */
+ private void handleScreenMagnificationEnabledSettingChangedLocked() {
+ mIsScreenMagnificationEnabled = Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
+ }
+
private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked() {
final int serviceCount = mServices.size();
for (int i = 0; i < serviceCount; i++) {
@@ -1083,7 +1107,12 @@
}
}
- private class MainHanler extends Handler {
+ private class MainHandler extends Handler {
+
+ public MainHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
final int type = msg.what;
@@ -1140,10 +1169,50 @@
case MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER: {
AccessibilityEvent event = (AccessibilityEvent) msg.obj;
if (mHasInputFilter && mInputFilter != null) {
- mInputFilter.onAccessibilityEvent(event);
+ mInputFilter.notifyAccessibilityEvent(event);
}
event.recycle();
} break;
+ case MSG_SEND_UPDATE_INPUT_FILTER: {
+ boolean setInputFilter = false;
+ AccessibilityInputFilter inputFilter = null;
+ synchronized (mLock) {
+ if ((mIsAccessibilityEnabled && mIsTouchExplorationEnabled)
+ || mIsScreenMagnificationEnabled) {
+ if (!mHasInputFilter) {
+ mHasInputFilter = true;
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext,
+ AccessibilityManagerService.this);
+ }
+ inputFilter = mInputFilter;
+ setInputFilter = true;
+ }
+ int flags = 0;
+ if (mIsScreenMagnificationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
+ }
+ if (mIsTouchExplorationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
+ }
+ mInputFilter.setEnabledFeatures(flags);
+ } else {
+ if (mHasInputFilter) {
+ mHasInputFilter = false;
+ mInputFilter.setEnabledFeatures(0);
+ inputFilter = null;
+ setInputFilter = true;
+ }
+ }
+ }
+ if (setInputFilter) {
+ try {
+ mWindowManager.setInputFilter(inputFilter);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } break;
}
}
}
@@ -1629,18 +1698,7 @@
// the state based on values in the settings database.
if (mIsAutomation) {
mUiAutomationService = null;
-
- populateEnabledAccessibilityServicesLocked();
- populateTouchExplorationGrantedAccessibilityServicesLocked();
-
- handleAccessibilityEnabledSettingChangedLocked();
- sendStateToClientsLocked();
-
- handleTouchExplorationEnabledSettingChangedLocked();
- updateInputFilterLocked();
-
- populateAccessibilityServiceListLocked();
- manageServicesLocked();
+ updateInternalStateLocked();
}
}
}
diff --git a/services/java/com/android/server/accessibility/EventStreamTransformation.java b/services/java/com/android/server/accessibility/EventStreamTransformation.java
new file mode 100644
index 0000000..b715570
--- /dev/null
+++ b/services/java/com/android/server/accessibility/EventStreamTransformation.java
@@ -0,0 +1,90 @@
+/*
+ ** 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.
+ */
+
+package com.android.server.accessibility;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Interface for classes that can handle and potentially transform a stream of
+ * motion and accessibility events. Instances implementing this interface are
+ * ordered in a sequence to implement a transformation chain. An instance may
+ * consume, modify, and generate events. It is responsible to deliver the
+ * output events to the next transformation in the sequence set via
+ * {@link #setNext(EventStreamTransformation)}.
+ *
+ * Note that since instances implementing this interface are transformations
+ * of the event stream, an instance should work against the event stream
+ * potentially modified by previous ones. Hence, the order of transformations
+ * is important.
+ *
+ * It is a responsibility of each handler that decides to react to an event
+ * sequence and prevent any subsequent ones from performing an action to send
+ * the appropriate cancel event given it has delegated a part of the events
+ * that belong to the current gesture. This will ensure that subsequent
+ * transformations will not be left in an inconsistent state and the applications
+ * see a consistent event stream.
+ *
+ * For example, to cancel a {@link KeyEvent} the handler has to emit an event
+ * with action {@link KeyEvent#ACTION_UP} with the additional flag
+ * {@link KeyEvent#FLAG_CANCELED}. To cancel a {@link MotionEvent} the handler
+ * has to send an event with action {@link MotionEvent#ACTION_CANCEL}.
+ *
+ * It is a responsibility of each handler that received a cancel event to clear its
+ * internal state and to propagate the event to the next one to enable subsequent
+ * transformations to clear their internal state.
+ *
+ * It is a responsibility for each transformation to start handling events only
+ * after an event that designates the start of a well-formed event sequence.
+ * For example, if it received a down motion event followed by a cancel motion
+ * event, it should not handle subsequent move and up events until it gets a down.
+ */
+interface EventStreamTransformation {
+
+ /**
+ * Receives a motion event.
+ *
+ * @param event The motion event.
+ * @param policyFlags Policy flags for the event.
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags);
+
+ /**
+ * Receives an accessibility event.
+ *
+ * @param event The accessibility event.
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event);
+
+ /**
+ * Sets the next transformation.
+ *
+ * @param next The next transformation.
+ */
+ public void setNext(EventStreamTransformation next);
+
+ /**
+ * Clears the internal state of this transformation.
+ */
+ public void clear();
+
+ /**
+ * Destroys this transformation.
+ */
+ public void onDestroy();
+}
diff --git a/services/java/com/android/server/accessibility/GestureUtils.java b/services/java/com/android/server/accessibility/GestureUtils.java
new file mode 100644
index 0000000..b68b09f
--- /dev/null
+++ b/services/java/com/android/server/accessibility/GestureUtils.java
@@ -0,0 +1,102 @@
+package com.android.server.accessibility;
+
+import android.util.MathUtils;
+import android.view.MotionEvent;
+
+/**
+ * Some helper functions for gesture detection.
+ */
+final class GestureUtils {
+
+ private GestureUtils() {
+ /* cannot be instantiated */
+ }
+
+ public static boolean isTap(MotionEvent down, MotionEvent up, int tapTimeSlop,
+ int tapDistanceSlop, int actionIndex) {
+ return eventsWithinTimeAndDistanceSlop(down, up, tapTimeSlop, tapDistanceSlop, actionIndex);
+ }
+
+ public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
+ int multiTapTimeSlop, int multiTapDistanceSlop, int actionIndex) {
+ return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
+ multiTapDistanceSlop, actionIndex);
+ }
+
+ private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
+ int timeout, int distance, int actionIndex) {
+ if (isTimedOut(first, second, timeout)) {
+ return false;
+ }
+ final double deltaMove = computeDistance(first, second, actionIndex);
+ if (deltaMove >= distance) {
+ return false;
+ }
+ return true;
+ }
+
+ public static double computeDistance(MotionEvent first, MotionEvent second, int pointerIndex) {
+ return MathUtils.dist(first.getX(pointerIndex), first.getY(pointerIndex),
+ second.getX(pointerIndex), second.getY(pointerIndex));
+ }
+
+ public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
+ final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
+ return (deltaTime >= timeout);
+ }
+
+ public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
+ return (first.getPointerIdBits() == second.getPointerIdBits()
+ && first.getPointerId(first.getActionIndex())
+ == second.getPointerId(second.getActionIndex()));
+ }
+
+ /**
+ * Determines whether a two pointer gesture is a dragging one.
+ *
+ * @param event The event with the pointer data.
+ * @return True if the gesture is a dragging one.
+ */
+ public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
+ float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
+ float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
+
+ // Check if the pointers are moving in the same direction.
+ final float firstDeltaX = firstPtrX - firstPtrDownX;
+ final float firstDeltaY = firstPtrY - firstPtrDownY;
+
+ if (firstDeltaX == 0 && firstDeltaY == 0) {
+ return true;
+ }
+
+ final float firstMagnitude =
+ (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
+ final float firstXNormalized =
+ (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
+ final float firstYNormalized =
+ (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
+
+ final float secondDeltaX = secondPtrX - secondPtrDownX;
+ final float secondDeltaY = secondPtrY - secondPtrDownY;
+
+ if (secondDeltaX == 0 && secondDeltaY == 0) {
+ return true;
+ }
+
+ final float secondMagnitude =
+ (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
+ final float secondXNormalized =
+ (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
+ final float secondYNormalized =
+ (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
+
+ final float angleCos =
+ firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
+
+ if (angleCos < maxDraggingAngleCos) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java
new file mode 100644
index 0000000..bd7f276
--- /dev/null
+++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -0,0 +1,1754 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.accessibility;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.MathUtils;
+import android.util.Property;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IDisplayContentChangeListener;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+
+/**
+ * This class handles the screen magnification when accessibility is enabled.
+ * The behavior is as follows:
+ *
+ * 1. Triple tap toggles permanent screen magnification which is magnifying
+ * the area around the location of the triple tap. One can think of the
+ * location of the triple tap as the center of the magnified viewport.
+ * For example, a triple tap when not magnified would magnify the screen
+ * and leave it in a magnified state. A triple tapping when magnified would
+ * clear magnification and leave the screen in a not magnified state.
+ *
+ * 2. Triple tap and hold would magnify the screen if not magnified and enable
+ * viewport dragging mode until the finger goes up. One can think of this
+ * mode as a way to move the magnified viewport since the area around the
+ * moving finger will be magnified to fit the screen. For example, if the
+ * screen was not magnified and the user triple taps and holds the screen
+ * would magnify and the viewport will follow the user's finger. When the
+ * finger goes up the screen will clear zoom out. If the same user interaction
+ * is performed when the screen is magnified, the viewport movement will
+ * be the same but when the finger goes up the screen will stay magnified.
+ * In other words, the initial magnified state is sticky.
+ *
+ * 3. Pinching with any number of additional fingers when viewport dragging
+ * is enabled, i.e. the user triple tapped and holds, would adjust the
+ * magnification scale which will become the current default magnification
+ * scale. The next time the user magnifies the same magnification scale
+ * would be used.
+ *
+ * 4. When in a permanent magnified state the user can use two or more fingers
+ * to pan the viewport. Note that in this mode the content is panned as
+ * opposed to the viewport dragging mode in which the viewport is moved.
+ *
+ * 5. When in a permanent magnified state the user can use three or more
+ * fingers to change the magnification scale which will become the current
+ * default magnification scale. The next time the user magnifies the same
+ * magnification scale would be used.
+ *
+ * 6. The magnification scale will be persisted in settings and in the cloud.
+ */
+public final class ScreenMagnifier implements EventStreamTransformation {
+
+ private static final boolean DEBUG_STATE_TRANSITIONS = false;
+ private static final boolean DEBUG_DETECTING = false;
+ private static final boolean DEBUG_TRANSFORMATION = false;
+ private static final boolean DEBUG_PANNING = false;
+ private static final boolean DEBUG_SCALING = false;
+ private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+ private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+ private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_GESTURE_DETECTOR = false;
+ private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false;
+
+ private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
+
+ private static final int STATE_DELEGATING = 1;
+ private static final int STATE_DETECTING = 2;
+ private static final int STATE_SCALING = 3;
+ private static final int STATE_VIEWPORT_DRAGGING = 4;
+ private static final int STATE_PANNING = 5;
+ private static final int STATE_DECIDE_PAN_OR_SCALE = 6;
+
+ private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
+ private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1;
+ private static final float DEFAULT_WINDOW_ANIMATION_SCALE = 1.0f;
+
+ private final IWindowManager mWindowManagerService = IWindowManager.Stub.asInterface(
+ ServiceManager.getService("window"));
+ private final WindowManager mWindowManager;
+ private final DisplayProvider mDisplayProvider;
+
+ private final DetectingStateHandler mDetectingStateHandler = new DetectingStateHandler();
+ private final GestureDetector mGestureDetector;
+ private final StateViewportDraggingHandler mStateViewportDraggingHandler =
+ new StateViewportDraggingHandler();
+
+ private final Interpolator mInterpolator = new DecelerateInterpolator(2.5f);
+
+ private final MagnificationController mMagnificationController;
+ private final DisplayContentObserver mDisplayContentObserver;
+ private final Viewport mViewport;
+
+ private final int mTapTimeSlop = ViewConfiguration.getTapTimeout();
+ private final int mMultiTapTimeSlop = ViewConfiguration.getDoubleTapTimeout();
+ private final int mTapDistanceSlop;
+ private final int mMultiTapDistanceSlop;
+
+ private final int mShortAnimationDuration;
+ private final int mLongAnimationDuration;
+ private final float mWindowAnimationScale;
+
+ private final Context mContext;
+
+ private EventStreamTransformation mNext;
+
+ private int mCurrentState;
+ private boolean mTranslationEnabledBeforePan;
+
+ private PointerCoords[] mTempPointerCoords;
+ private PointerProperties[] mTempPointerProperties;
+
+ public ScreenMagnifier(Context context) {
+ mContext = context;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+
+ mShortAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+ mLongAnimationDuration = context.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ mTapDistanceSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mMultiTapDistanceSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.WINDOW_ANIMATION_SCALE, DEFAULT_WINDOW_ANIMATION_SCALE);
+
+ mMagnificationController = new MagnificationController(mShortAnimationDuration);
+ mDisplayProvider = new DisplayProvider(context, mWindowManager);
+ mViewport = new Viewport(mContext, mWindowManager, mWindowManagerService,
+ mDisplayProvider, mInterpolator, mShortAnimationDuration);
+ mDisplayContentObserver = new DisplayContentObserver(mContext, mViewport,
+ mMagnificationController, mWindowManagerService, mDisplayProvider,
+ mLongAnimationDuration, mWindowAnimationScale);
+
+ mGestureDetector = new GestureDetector(context);
+
+ transitionToState(STATE_DETECTING);
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ switch (mCurrentState) {
+ case STATE_DELEGATING: {
+ handleMotionEventStateDelegating(event, policyFlags);
+ } break;
+ case STATE_DETECTING: {
+ mDetectingStateHandler.onMotionEvent(event, policyFlags);
+ } break;
+ case STATE_VIEWPORT_DRAGGING: {
+ mStateViewportDraggingHandler.onMotionEvent(event, policyFlags);
+ } break;
+ case STATE_SCALING:
+ case STATE_PANNING:
+ case STATE_DECIDE_PAN_OR_SCALE: {
+ // Handled by the gesture detector. Since the detector
+ // needs all touch events to work properly we cannot
+ // call it only for these states.
+ } break;
+ default: {
+ throw new IllegalStateException("Unknown state: " + mCurrentState);
+ }
+ }
+ mGestureDetector.onMotionEvent(event);
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
+ }
+
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
+ public void clear() {
+ mCurrentState = STATE_DETECTING;
+ mDetectingStateHandler.clear();
+ mStateViewportDraggingHandler.clear();
+ mGestureDetector.clear();
+
+ if (mNext != null) {
+ mNext.clear();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ mDisplayProvider.destroy();
+ mDisplayContentObserver.destroy();
+ }
+
+ private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mDetectingStateHandler.mDelayedEventQueue == null) {
+ transitionToState(STATE_DETECTING);
+ }
+ }
+ if (mNext != null) {
+ // If the event is within the magnified portion of the screen we have
+ // to change its location to be where the user thinks he is poking the
+ // UI which may have been magnified and panned.
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mMagnificationController.isMagnifying()
+ && mViewport.getBounds().contains((int) eventX, (int) eventY)) {
+ final float scale = mMagnificationController.getScale();
+ final float scaledOffsetX = mMagnificationController.getScaledOffsetX();
+ final float scaledOffsetY = mMagnificationController.getScaledOffsetY();
+ final int pointerCount = event.getPointerCount();
+ PointerCoords[] coords = getTempPointerCoordsWithMinSize(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;
+ coords[i].y = (coords[i].y - scaledOffsetY) / scale;
+ event.getPointerProperties(i, properties[i]);
+ }
+ event = MotionEvent.obtain(event.getDownTime(),
+ event.getEventTime(), event.getAction(), pointerCount, properties,
+ coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
+ event.getFlags());
+ }
+ mNext.onMotionEvent(event, policyFlags);
+ }
+ }
+
+ private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
+ final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0;
+ if (oldSize < size) {
+ mTempPointerCoords = new PointerCoords[size];
+ }
+ for (int i = oldSize; i < size; i++) {
+ mTempPointerCoords[i] = new PointerCoords();
+ }
+ return mTempPointerCoords;
+ }
+
+ private PointerProperties[] getTempPointerPropertiesWithMinSize(int size) {
+ final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length : 0;
+ if (oldSize < size) {
+ mTempPointerProperties = new PointerProperties[size];
+ }
+ for (int i = oldSize; i < size; i++) {
+ mTempPointerProperties[i] = new PointerProperties();
+ }
+ return mTempPointerProperties;
+ }
+
+ private void transitionToState(int state) {
+ if (DEBUG_STATE_TRANSITIONS) {
+ switch (state) {
+ case STATE_DELEGATING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_DELEGATING");
+ } break;
+ case STATE_DETECTING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING");
+ } break;
+ case STATE_VIEWPORT_DRAGGING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_VIEWPORT_DRAGGING");
+ } break;
+ case STATE_SCALING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_SCALING");
+ } break;
+ case STATE_PANNING: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_PANNING");
+ } break;
+ case STATE_DECIDE_PAN_OR_SCALE: {
+ Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING_PAN_OR_SCALE");
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown state: " + state);
+ }
+ }
+ }
+ mCurrentState = state;
+ }
+
+ private final class GestureDetector implements OnScaleGestureListener {
+ private static final float MIN_SCALE = 1.3f;
+ private static final float MAX_SCALE = 5.0f;
+
+ private static final float DETECT_SCALING_THRESHOLD = 0.25f;
+ private static final int DETECT_PANNING_THRESHOLD_DIP = 30;
+
+ private final float mScaledDetectPanningThreshold;
+
+ private final ScaleGestureDetector mScaleGestureDetector;
+
+ private final PointF mPrevFocus = new PointF(Float.NaN, Float.NaN);
+ private final PointF mInitialFocus = new PointF(Float.NaN, Float.NaN);
+
+ private float mCurrScale = Float.NaN;
+ private float mCurrScaleFactor = 1.0f;
+ private float mPrevScaleFactor = 1.0f;
+ private float mCurrPan;
+ private float mPrevPan;
+
+ private float mScaleFocusX = Float.NaN;
+ private float mScaleFocusY = Float.NaN;
+
+ public GestureDetector(Context context) {
+ final float density = context.getResources().getDisplayMetrics().density;
+ mScaledDetectPanningThreshold = DETECT_PANNING_THRESHOLD_DIP * density;
+ mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ }
+
+ public void onMotionEvent(MotionEvent event) {
+ mScaleGestureDetector.onTouchEvent(event);
+ switch (mCurrentState) {
+ case STATE_DETECTING:
+ case STATE_DELEGATING:
+ case STATE_VIEWPORT_DRAGGING: {
+ return;
+ }
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ clear();
+ if (mCurrentState == STATE_SCALING) {
+ persistScale(mMagnificationController.getScale());
+ }
+ transitionToState(STATE_DETECTING);
+ }
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ switch (mCurrentState) {
+ case STATE_DETECTING:
+ case STATE_DELEGATING:
+ case STATE_VIEWPORT_DRAGGING: {
+ return true;
+ }
+ case STATE_DECIDE_PAN_OR_SCALE: {
+ mCurrScaleFactor = mScaleGestureDetector.getScaleFactor();
+ final float scaleDelta = Math.abs(1.0f - mCurrScaleFactor * mPrevScaleFactor);
+ if (DEBUG_GESTURE_DETECTOR) {
+ Slog.i(LOG_TAG, "scaleDelta: " + scaleDelta);
+ }
+ if (scaleDelta > DETECT_SCALING_THRESHOLD) {
+ performScale(detector, true);
+ transitionToState(STATE_SCALING);
+ return false;
+ }
+ mCurrPan = (float) MathUtils.dist(
+ mScaleGestureDetector.getFocusX(),
+ mScaleGestureDetector.getFocusY(),
+ mInitialFocus.x, mInitialFocus.y);
+ final float panDelta = mCurrPan + mPrevPan;
+ if (DEBUG_GESTURE_DETECTOR) {
+ Slog.i(LOG_TAG, "panDelta: " + panDelta);
+ }
+ if (panDelta > mScaledDetectPanningThreshold) {
+ transitionToState(STATE_PANNING);
+ performPan(detector, true);
+ return false;
+ }
+ } break;
+ case STATE_SCALING: {
+ performScale(detector, false);
+ } break;
+ case STATE_PANNING: {
+ performPan(detector, false);
+ } break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ switch (mCurrentState) {
+ case STATE_DECIDE_PAN_OR_SCALE: {
+ mPrevScaleFactor *= mCurrScaleFactor;
+ mPrevPan += mCurrPan;
+ mPrevFocus.x = mInitialFocus.x = detector.getFocusX();
+ mPrevFocus.y = mInitialFocus.y = detector.getFocusY();
+ } break;
+ case STATE_SCALING: {
+ mPrevScaleFactor = 1.0f;
+ mCurrScale = Float.NaN;
+ } break;
+ case STATE_PANNING: {
+ mPrevPan += mCurrPan;
+ mPrevFocus.x = mInitialFocus.x = detector.getFocusX();
+ mPrevFocus.y = mInitialFocus.y = detector.getFocusY();
+ } break;
+ }
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ /* do nothing */
+ }
+
+ public void clear() {
+ mCurrScaleFactor = 1.0f;
+ mPrevScaleFactor = 1.0f;
+ mPrevPan = 0;
+ mCurrPan = 0;
+ mInitialFocus.set(Float.NaN, Float.NaN);
+ mPrevFocus.set(Float.NaN, Float.NaN);
+ mCurrScale = Float.NaN;
+ mScaleFocusX = Float.NaN;
+ mScaleFocusY = Float.NaN;
+ }
+
+ private void performPan(ScaleGestureDetector detector, boolean animate) {
+ if (Float.compare(mPrevFocus.x, Float.NaN) == 0
+ && Float.compare(mPrevFocus.y, Float.NaN) == 0) {
+ mPrevFocus.set(detector.getFocusX(), detector.getFocusY());
+ return;
+ }
+ final float scale = mMagnificationController.getScale();
+ final float scrollX = (detector.getFocusX() - mPrevFocus.x) / scale;
+ final float scrollY = (detector.getFocusY() - mPrevFocus.y) / scale;
+ final float centerX = mMagnificationController.getMagnifiedRegionCenterX()
+ - scrollX;
+ final float centerY = mMagnificationController.getMagnifiedRegionCenterY()
+ - scrollY;
+ if (DEBUG_PANNING) {
+ Slog.i(LOG_TAG, "Panned content by scrollX: " + scrollX
+ + " scrollY: " + scrollY);
+ }
+ mMagnificationController.setMagnifiedRegionCenter(centerX, centerY, animate);
+ mPrevFocus.set(detector.getFocusX(), detector.getFocusY());
+ }
+
+ private void performScale(ScaleGestureDetector detector, boolean animate) {
+ if (Float.compare(mCurrScale, Float.NaN) == 0) {
+ mCurrScale = mMagnificationController.getScale();
+ return;
+ }
+ final float totalScaleFactor = mPrevScaleFactor * detector.getScaleFactor();
+ final float newScale = mCurrScale * totalScaleFactor;
+ final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE),
+ MAX_SCALE);
+ if (DEBUG_SCALING) {
+ Slog.i(LOG_TAG, "normalizedNewScale: " + normalizedNewScale);
+ }
+ if (Float.compare(mScaleFocusX, Float.NaN) == 0
+ && Float.compare(mScaleFocusY, Float.NaN) == 0) {
+ mScaleFocusX = detector.getFocusX();
+ mScaleFocusY = detector.getFocusY();
+ }
+ mMagnificationController.setScale(normalizedNewScale, mScaleFocusX,
+ mScaleFocusY, animate);
+ }
+ }
+
+ private final class StateViewportDraggingHandler {
+ private boolean mLastMoveOutsideMagnifiedRegion;
+
+ private void onMotionEvent(MotionEvent event, int policyFlags) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalArgumentException("Unexpected event type: ACTION_DOWN");
+ }
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ clear();
+ transitionToState(STATE_SCALING);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ if (event.getPointerCount() != 1) {
+ throw new IllegalStateException("Should have one pointer down.");
+ }
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mViewport.getBounds().contains((int) eventX, (int) eventY)) {
+ if (mLastMoveOutsideMagnifiedRegion) {
+ mLastMoveOutsideMagnifiedRegion = false;
+ mMagnificationController.setMagnifiedRegionCenter(eventX,
+ eventY, true);
+ } else {
+ mMagnificationController.setMagnifiedRegionCenter(eventX,
+ eventY, false);
+ }
+ } else {
+ mLastMoveOutsideMagnifiedRegion = true;
+ }
+ } break;
+ case MotionEvent.ACTION_UP: {
+ if (!mTranslationEnabledBeforePan) {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ clear();
+ transitionToState(STATE_DETECTING);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ throw new IllegalArgumentException("Unexpected event type: ACTION_POINTER_UP");
+ }
+ }
+ }
+
+ public void clear() {
+ mLastMoveOutsideMagnifiedRegion = false;
+ }
+ }
+
+ private final class DetectingStateHandler {
+
+ private static final int MESSAGE_ON_ACTION_TAP_AND_HOLD = 1;
+
+ private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
+
+ private static final int ACTION_TAP_COUNT = 3;
+
+ private MotionEventInfo mDelayedEventQueue;
+
+ private MotionEvent mLastDownEvent;
+ private MotionEvent mLastTapUpEvent;
+ private int mTapCount;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ final int type = message.what;
+ switch (type) {
+ case MESSAGE_ON_ACTION_TAP_AND_HOLD: {
+ MotionEvent event = (MotionEvent) message.obj;
+ final int policyFlags = message.arg1;
+ onActionTapAndHold(event, policyFlags);
+ } break;
+ case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
+ transitionToState(STATE_DELEGATING);
+ sendDelayedMotionEvents();
+ clear();
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown message type: " + type);
+ }
+ }
+ }
+ };
+
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ cacheDelayedMotionEvent(event, policyFlags);
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ if (!mViewport.getBounds().contains((int) event.getX(),
+ (int) event.getY())) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null
+ && GestureUtils.isMultiTap(mLastDownEvent, event,
+ mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
+ policyFlags, 0, event);
+ mHandler.sendMessageDelayed(message,
+ ViewConfiguration.getLongPressTimeout());
+ } else if (mTapCount < ACTION_TAP_COUNT) {
+ Message message = mHandler.obtainMessage(
+ MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mHandler.sendMessageDelayed(message, mTapTimeSlop + mMultiTapDistanceSlop);
+ }
+ clearLastDownEvent();
+ mLastDownEvent = MotionEvent.obtain(event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ if (mMagnificationController.isMagnifying()) {
+ transitionToState(STATE_DECIDE_PAN_OR_SCALE);
+ clear();
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ if (mLastDownEvent != null && mTapCount < ACTION_TAP_COUNT - 1) {
+ final double distance = GestureUtils.computeDistance(mLastDownEvent,
+ event, 0);
+ if (Math.abs(distance) > mTapDistanceSlop) {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_UP: {
+ if (mLastDownEvent == null) {
+ return;
+ }
+ mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
+ if (!mViewport.getBounds().contains((int) event.getX(), (int) event.getY())) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
+ mTapDistanceSlop, 0)) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent,
+ event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+ transitionToDelegatingStateAndClear();
+ return;
+ }
+ mTapCount++;
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "Tap count:" + mTapCount);
+ }
+ if (mTapCount == ACTION_TAP_COUNT) {
+ clear();
+ onActionTap(event, policyFlags);
+ return;
+ }
+ clearLastTapUpEvent();
+ mLastTapUpEvent = MotionEvent.obtain(event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ /* do nothing */
+ } break;
+ }
+ }
+
+ public void clear() {
+ mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ clearTapDetectionState();
+ clearDelayedMotionEvents();
+ }
+
+ private void clearTapDetectionState() {
+ mTapCount = 0;
+ clearLastTapUpEvent();
+ clearLastDownEvent();
+ }
+
+ private void clearLastTapUpEvent() {
+ if (mLastTapUpEvent != null) {
+ mLastTapUpEvent.recycle();
+ mLastTapUpEvent = null;
+ }
+ }
+
+ private void clearLastDownEvent() {
+ if (mLastDownEvent != null) {
+ mLastDownEvent.recycle();
+ mLastDownEvent = null;
+ }
+ }
+
+ private void cacheDelayedMotionEvent(MotionEvent event, int policyFlags) {
+ MotionEventInfo info = MotionEventInfo.obtain(event, policyFlags);
+ if (mDelayedEventQueue == null) {
+ mDelayedEventQueue = info;
+ } else {
+ MotionEventInfo tail = mDelayedEventQueue;
+ while (tail.mNext != null) {
+ tail = tail.mNext;
+ }
+ tail.mNext = info;
+ }
+ }
+
+ private void sendDelayedMotionEvents() {
+ while (mDelayedEventQueue != null) {
+ MotionEventInfo info = mDelayedEventQueue;
+ mDelayedEventQueue = info.mNext;
+ ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mPolicyFlags);
+ info.recycle();
+ }
+ }
+
+ private void clearDelayedMotionEvents() {
+ while (mDelayedEventQueue != null) {
+ MotionEventInfo info = mDelayedEventQueue;
+ mDelayedEventQueue = info.mNext;
+ info.recycle();
+ }
+ }
+
+ private void transitionToDelegatingStateAndClear() {
+ transitionToState(STATE_DELEGATING);
+ sendDelayedMotionEvents();
+ clear();
+ }
+
+ private void onActionTap(MotionEvent up, int policyFlags) {
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "onActionTap()");
+ }
+ if (!mMagnificationController.isMagnifying()) {
+ mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
+ up.getX(), up.getY(), true);
+ mViewport.setFrameShown(true, true);
+ } else {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ }
+
+ private void onActionTapAndHold(MotionEvent down, int policyFlags) {
+ if (DEBUG_DETECTING) {
+ Slog.i(LOG_TAG, "onActionTapAndHold()");
+ }
+ clear();
+ mTranslationEnabledBeforePan = mMagnificationController.isMagnifying();
+ mMagnificationController.setScaleAndMagnifiedRegionCenter(getPersistedScale(),
+ down.getX(), down.getY(), true);
+ mViewport.setFrameShown(true, true);
+ transitionToState(STATE_VIEWPORT_DRAGGING);
+ }
+ }
+
+ private void persistScale(final float scale) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale);
+ return null;
+ }
+ }.execute();
+ }
+
+ private float getPersistedScale() {
+ return Settings.Secure.getFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+ DEFAULT_MAGNIFICATION_SCALE);
+ }
+
+ 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 int mPolicyFlags;
+
+ public static MotionEventInfo obtain(MotionEvent event, int policyFlags) {
+ synchronized (sLock) {
+ MotionEventInfo info;
+ if (sPoolSize > 0) {
+ sPoolSize--;
+ info = sPool;
+ sPool = info.mNext;
+ info.mNext = null;
+ info.mInPool = false;
+ } else {
+ info = new MotionEventInfo();
+ }
+ info.initialize(event, policyFlags);
+ return info;
+ }
+ }
+
+ private void initialize(MotionEvent event, int policyFlags) {
+ mEvent = MotionEvent.obtain(event);
+ mPolicyFlags = policyFlags;
+ }
+
+ public void recycle() {
+ synchronized (sLock) {
+ if (mInPool) {
+ throw new IllegalStateException("Already recycled.");
+ }
+ clear();
+ if (sPoolSize < MAX_POOL_SIZE) {
+ sPoolSize++;
+ mNext = sPool;
+ sPool = this;
+ mInPool = true;
+ }
+ }
+ }
+
+ private void clear() {
+ mEvent.recycle();
+ mEvent = null;
+ mPolicyFlags = 0;
+ }
+ }
+
+ private static final class DisplayContentObserver {
+
+ private static final int MESSAGE_SHOW_VIEWPORT_FRAME = 1;
+ private static final int MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS = 2;
+ private static final int MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED = 3;
+ private static final int MESSAGE_ON_WINDOW_TRANSITION = 4;
+ private static final int MESSAGE_ON_ROTATION_CHANGED = 5;
+
+ private final Handler mHandler = new MyHandler();
+
+ private final Rect mTempRect = new Rect();
+
+ private final IDisplayContentChangeListener mDisplayContentChangeListener;
+
+ private final Context mContext;
+ private final Viewport mViewport;
+ private final MagnificationController mMagnificationController;
+ private final IWindowManager mWindowManagerService;
+ private final DisplayProvider mDisplayProvider;
+ private final long mLongAnimationDuration;
+ private final float mWindowAnimationScale;
+
+ public DisplayContentObserver(Context context, Viewport viewport,
+ MagnificationController magnificationController,
+ IWindowManager windowManagerService, DisplayProvider displayProvider,
+ long longAnimationDuration, float windowAnimationScale) {
+ mContext = context;
+ mViewport = viewport;
+ mMagnificationController = magnificationController;
+ mWindowManagerService = windowManagerService;
+ mDisplayProvider = displayProvider;
+ mLongAnimationDuration = longAnimationDuration;
+ mWindowAnimationScale = windowAnimationScale;
+
+ mDisplayContentChangeListener = new IDisplayContentChangeListener.Stub() {
+ @Override
+ public void onWindowTransition(int displayId, int transition, WindowInfo info) {
+ mHandler.obtainMessage(MESSAGE_ON_WINDOW_TRANSITION, transition, 0,
+ WindowInfo.obtain(info)).sendToTarget();
+ }
+
+ @Override
+ public void onRectangleOnScreenRequested(int dsiplayId, Rect rectangle,
+ boolean immediate) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = rectangle.left;
+ args.argi2 = rectangle.top;
+ args.argi3 = rectangle.right;
+ args.argi4 = rectangle.bottom;
+ mHandler.obtainMessage(MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED, 0,
+ immediate ? 1 : 0, args).sendToTarget();
+ }
+
+ @Override
+ public void onRotationChanged(int rotation) throws RemoteException {
+ mHandler.obtainMessage(MESSAGE_ON_ROTATION_CHANGED, rotation, 0)
+ .sendToTarget();
+ }
+ };
+
+ try {
+ mWindowManagerService.addDisplayContentChangeListener(
+ mDisplayProvider.getDisplay().getDisplayId(),
+ mDisplayContentChangeListener);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ public void destroy() {
+ try {
+ mWindowManagerService.removeDisplayContentChangeListener(
+ mDisplayProvider.getDisplay().getDisplayId(),
+ mDisplayContentChangeListener);
+ } catch (RemoteException re) {
+ /* ignore*/
+ }
+ }
+
+ private void handleOnRotationChanged(int rotation) {
+ if (DEBUG_ROTATION) {
+ Slog.i(LOG_TAG, "Rotation: " + rotationToString(rotation));
+ }
+ resetMagnificationIfNeeded();
+ mViewport.setFrameShown(false, false);
+ mViewport.rotationChanged();
+ mViewport.recomputeBounds(false);
+ if (mMagnificationController.isMagnifying()) {
+ final long delay = (long) (2 * mLongAnimationDuration * mWindowAnimationScale);
+ Message message = mHandler.obtainMessage(MESSAGE_SHOW_VIEWPORT_FRAME);
+ mHandler.sendMessageDelayed(message, delay);
+ }
+ }
+
+ private void handleOnWindowTransition(int transition, WindowInfo info) {
+ if (DEBUG_WINDOW_TRANSITIONS) {
+ Slog.i(LOG_TAG, "Window transitioning: "
+ + windowTransitionToString(transition));
+ }
+ try {
+ final boolean magnifying = mMagnificationController.isMagnifying();
+ if (magnifying) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ resetMagnificationIfNeeded();
+ }
+ }
+ }
+ if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW:
+ case WindowManagerPolicy.TRANSIT_EXIT:
+ case WindowManagerPolicy.TRANSIT_HIDE: {
+ mViewport.recomputeBounds(mMagnificationController.isMagnifying());
+ } break;
+ }
+ } else {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ if (!magnifying || !screenMagnificationAutoUpdateEnabled(mContext)) {
+ break;
+ }
+ final int type = info.type;
+ switch (type) {
+ // TODO: Are these all the windows we want to make
+ // visible when they appear on the screen?
+ // Do we need to take some of them out?
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
+ Rect magnifiedRegionBounds = mMagnificationController
+ .getMagnifiedRegionBounds();
+ Rect touchableRegion = info.touchableRegion;
+ if (!magnifiedRegionBounds.intersect(touchableRegion)) {
+ ensureRectangleInMagnifiedRegionBounds(
+ magnifiedRegionBounds, touchableRegion);
+ }
+ } break;
+ } break;
+ }
+ }
+ }
+ } finally {
+ if (info != null) {
+ info.recycle();
+ }
+ }
+ }
+
+ private void handleOnRectangleOnScreenRequested(Rect rectangle, boolean immediate) {
+ if (!mMagnificationController.isMagnifying()) {
+ return;
+ }
+ Rect magnifiedRegionBounds = mMagnificationController.getMagnifiedRegionBounds();
+ if (magnifiedRegionBounds.contains(rectangle)) {
+ return;
+ }
+ ensureRectangleInMagnifiedRegionBounds(magnifiedRegionBounds, rectangle);
+ }
+
+ private void ensureRectangleInMagnifiedRegionBounds(Rect magnifiedRegionBounds,
+ Rect rectangle) {
+ if (!Rect.intersects(rectangle, mViewport.getBounds())) {
+ return;
+ }
+ final float scrollX;
+ final float scrollY;
+ if (rectangle.width() > magnifiedRegionBounds.width()) {
+ scrollX = rectangle.left - magnifiedRegionBounds.left;
+ } else if (rectangle.left < magnifiedRegionBounds.left) {
+ scrollX = rectangle.left - magnifiedRegionBounds.left;
+ } else if (rectangle.right > magnifiedRegionBounds.right) {
+ scrollX = rectangle.right - magnifiedRegionBounds.right;
+ } else {
+ scrollX = 0;
+ }
+ if (rectangle.height() > magnifiedRegionBounds.height()) {
+ scrollY = rectangle.top - magnifiedRegionBounds.top;
+ } else if (rectangle.top < magnifiedRegionBounds.top) {
+ scrollY = rectangle.top - magnifiedRegionBounds.top;
+ } else if (rectangle.bottom > magnifiedRegionBounds.bottom) {
+ scrollY = rectangle.bottom - magnifiedRegionBounds.bottom;
+ } else {
+ scrollY = 0;
+ }
+ final float viewportCenterX = mMagnificationController.getMagnifiedRegionCenterX()
+ + scrollX;
+ final float viewportCenterY = mMagnificationController.getMagnifiedRegionCenterY()
+ + scrollY;
+ mMagnificationController.setMagnifiedRegionCenter(viewportCenterX, viewportCenterY,
+ true);
+ }
+
+ private void resetMagnificationIfNeeded() {
+ if (mMagnificationController.isMagnifying()
+ && screenMagnificationAutoUpdateEnabled(mContext)) {
+ mMagnificationController.reset(true);
+ mViewport.setFrameShown(false, true);
+ }
+ }
+
+ private boolean screenMagnificationAutoUpdateEnabled(Context context) {
+ return (Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE) == 1);
+ }
+
+ private String windowTransitionToString(int transition) {
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_UNSET: {
+ return "TRANSIT_UNSET";
+ }
+ case WindowManagerPolicy.TRANSIT_NONE: {
+ return "TRANSIT_NONE";
+ }
+ case WindowManagerPolicy.TRANSIT_ENTER: {
+ return "TRANSIT_ENTER";
+ }
+ case WindowManagerPolicy.TRANSIT_EXIT: {
+ return "TRANSIT_EXIT";
+ }
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ return "TRANSIT_SHOW";
+ }
+ case WindowManagerPolicy.TRANSIT_EXIT_MASK: {
+ return "TRANSIT_EXIT_MASK";
+ }
+ case WindowManagerPolicy.TRANSIT_PREVIEW_DONE: {
+ return "TRANSIT_PREVIEW_DONE";
+ }
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: {
+ return "TRANSIT_ACTIVITY_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: {
+ return "TRANSIT_ACTIVITY_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN: {
+ return "TRANSIT_TASK_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE: {
+ return "TRANSIT_TASK_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT: {
+ return "TRANSIT_TASK_TO_FRONT";
+ }
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK: {
+ return "TRANSIT_TASK_TO_BACK";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE: {
+ return "TRANSIT_WALLPAPER_CLOSE";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN: {
+ return "TRANSIT_WALLPAPER_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ return "TRANSIT_WALLPAPER_INTRA_OPEN";
+ }
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE: {
+ return "TRANSIT_WALLPAPER_INTRA_CLOSE";
+ }
+ default: {
+ return "<UNKNOWN>";
+ }
+ }
+ }
+
+ private String rotationToString(int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0: {
+ return "ROTATION_0";
+ }
+ case Surface.ROTATION_90: {
+ return "ROATATION_90";
+ }
+ case Surface.ROTATION_180: {
+ return "ROATATION_180";
+ }
+ case Surface.ROTATION_270: {
+ return "ROATATION_270";
+ }
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: "
+ + rotation);
+ }
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ @Override
+ public void handleMessage(Message message) {
+ final int action = message.what;
+ switch (action) {
+ case MESSAGE_SHOW_VIEWPORT_FRAME: {
+ mViewport.setFrameShown(true, true);
+ } break;
+ case MESSAGE_RECOMPUTE_VIEWPORT_BOUNDS: {
+ final boolean animate = message.arg1 == 1;
+ mViewport.recomputeBounds(animate);
+ } break;
+ case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ try {
+ mTempRect.set(args.argi1, args.argi2, args.argi3, args.argi4);
+ final boolean immediate = (message.arg1 == 1);
+ handleOnRectangleOnScreenRequested(mTempRect, immediate);
+ } finally {
+ args.recycle();
+ }
+ } break;
+ case MESSAGE_ON_WINDOW_TRANSITION: {
+ final int transition = message.arg1;
+ WindowInfo info = (WindowInfo) message.obj;
+ handleOnWindowTransition(transition, info);
+ } break;
+ case MESSAGE_ON_ROTATION_CHANGED: {
+ final int rotation = message.arg1;
+ handleOnRotationChanged(rotation);
+ } break;
+ default: {
+ throw new IllegalArgumentException("Unknown message: " + action);
+ }
+ }
+ }
+ }
+ }
+
+ private final class MagnificationController {
+
+ private static final String PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION =
+ "accessibilityTransformation";
+
+ private final MagnificationSpec mSentMagnificationSpec = new MagnificationSpec();
+
+ private final MagnificationSpec mCurrentMagnificationSpec = new MagnificationSpec();
+
+ private final Rect mTempRect = new Rect();
+
+ private final ValueAnimator mTransformationAnimator;
+
+ public MagnificationController(int animationDuration) {
+ Property<MagnificationController, MagnificationSpec> property =
+ Property.of(MagnificationController.class, MagnificationSpec.class,
+ PROPERTY_NAME_ACCESSIBILITY_TRANSFORMATION);
+ TypeEvaluator<MagnificationSpec> evaluator = new TypeEvaluator<MagnificationSpec>() {
+ private final MagnificationSpec mTempTransformationSpec = new MagnificationSpec();
+ @Override
+ public MagnificationSpec evaluate(float fraction, MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ MagnificationSpec result = mTempTransformationSpec;
+ result.mScale = fromSpec.mScale
+ + (toSpec.mScale - fromSpec.mScale) * fraction;
+ result.mMagnifiedRegionCenterX = fromSpec.mMagnifiedRegionCenterX
+ + (toSpec.mMagnifiedRegionCenterX - fromSpec.mMagnifiedRegionCenterX)
+ * fraction;
+ result.mMagnifiedRegionCenterY = fromSpec.mMagnifiedRegionCenterY
+ + (toSpec.mMagnifiedRegionCenterY - fromSpec.mMagnifiedRegionCenterY)
+ * fraction;
+ result.mScaledOffsetX = fromSpec.mScaledOffsetX
+ + (toSpec.mScaledOffsetX - fromSpec.mScaledOffsetX)
+ * fraction;
+ result.mScaledOffsetY = fromSpec.mScaledOffsetY
+ + (toSpec.mScaledOffsetY - fromSpec.mScaledOffsetY)
+ * fraction;
+ return result;
+ }
+ };
+ mTransformationAnimator = ObjectAnimator.ofObject(this, property,
+ evaluator, mSentMagnificationSpec, mCurrentMagnificationSpec);
+ mTransformationAnimator.setDuration((long) (animationDuration));
+ mTransformationAnimator.setInterpolator(mInterpolator);
+ }
+
+ public boolean isMagnifying() {
+ return mCurrentMagnificationSpec.mScale > 1.0f;
+ }
+
+ public void reset(boolean animate) {
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ mCurrentMagnificationSpec.reset();
+ if (animate) {
+ animateAccessibilityTranformation(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setAccessibilityTransformation(mCurrentMagnificationSpec);
+ }
+ }
+
+ public Rect getMagnifiedRegionBounds() {
+ mTempRect.set(mViewport.getBounds());
+ mTempRect.offset((int) -mCurrentMagnificationSpec.mScaledOffsetX,
+ (int) -mCurrentMagnificationSpec.mScaledOffsetY);
+ mTempRect.scale(1.0f / mCurrentMagnificationSpec.mScale);
+ return mTempRect;
+ }
+
+ public float getScale() {
+ return mCurrentMagnificationSpec.mScale;
+ }
+
+ public float getMagnifiedRegionCenterX() {
+ return mCurrentMagnificationSpec.mMagnifiedRegionCenterX;
+ }
+
+ public float getMagnifiedRegionCenterY() {
+ return mCurrentMagnificationSpec.mMagnifiedRegionCenterY;
+ }
+
+ public float getScaledOffsetX() {
+ return mCurrentMagnificationSpec.mScaledOffsetX;
+ }
+
+ public float getScaledOffsetY() {
+ return mCurrentMagnificationSpec.mScaledOffsetY;
+ }
+
+ public void setScale(float scale, float pivotX, float pivotY, boolean animate) {
+ MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.mScale;
+ final float oldCenterX = spec.mMagnifiedRegionCenterX;
+ final float oldCenterY = spec.mMagnifiedRegionCenterY;
+ final float normPivotX = (-spec.mScaledOffsetX + pivotX) / oldScale;
+ final float normPivotY = (-spec.mScaledOffsetY + 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 void setMagnifiedRegionCenter(float centerX, float centerY, boolean animate) {
+ setScaleAndMagnifiedRegionCenter(mCurrentMagnificationSpec.mScale, centerX, centerY,
+ animate);
+ }
+
+ public void setScaleAndMagnifiedRegionCenter(float scale, float centerX, float centerY,
+ boolean animate) {
+ if (Float.compare(mCurrentMagnificationSpec.mScale, scale) == 0
+ && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterX,
+ centerX) == 0
+ && Float.compare(mCurrentMagnificationSpec.mMagnifiedRegionCenterY,
+ centerY) == 0) {
+ return;
+ }
+ if (mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ if (DEBUG_MAGNIFICATION_CONTROLLER) {
+ Slog.i(LOG_TAG, "scale: " + scale + " centerX: " + centerX
+ + " centerY: " + centerY);
+ }
+ mCurrentMagnificationSpec.initialize(scale, centerX, centerY);
+ if (animate) {
+ animateAccessibilityTranformation(mSentMagnificationSpec,
+ mCurrentMagnificationSpec);
+ } else {
+ setAccessibilityTransformation(mCurrentMagnificationSpec);
+ }
+ }
+
+ private void animateAccessibilityTranformation(MagnificationSpec fromSpec,
+ MagnificationSpec toSpec) {
+ mTransformationAnimator.setObjectValues(fromSpec, toSpec);
+ mTransformationAnimator.start();
+ }
+
+ @SuppressWarnings("unused")
+ // Called from an animator.
+ public MagnificationSpec getAccessibilityTransformation() {
+ return mSentMagnificationSpec;
+ }
+
+ public void setAccessibilityTransformation(MagnificationSpec transformation) {
+ if (DEBUG_TRANSFORMATION) {
+ Slog.i(LOG_TAG, "Transformation scale: " + transformation.mScale
+ + " offsetX: " + transformation.mScaledOffsetX
+ + " offsetY: " + transformation.mScaledOffsetY);
+ }
+ try {
+ mSentMagnificationSpec.updateFrom(transformation);
+ mWindowManagerService.magnifyDisplay(mDisplayProvider.getDisplay().getDisplayId(),
+ transformation.mScale, transformation.mScaledOffsetX,
+ transformation.mScaledOffsetY);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ private class MagnificationSpec {
+
+ private static final float DEFAULT_SCALE = 1.0f;
+
+ public float mScale = DEFAULT_SCALE;
+
+ public float mMagnifiedRegionCenterX;
+
+ public float mMagnifiedRegionCenterY;
+
+ public float mScaledOffsetX;
+
+ public float mScaledOffsetY;
+
+ public void initialize(float scale, float magnifiedRegionCenterX,
+ float magnifiedRegionCenterY) {
+ mScale = scale;
+
+ final int viewportWidth = mViewport.getBounds().width();
+ final int viewportHeight = mViewport.getBounds().height();
+ final float minMagnifiedRegionCenterX = (viewportWidth / 2) / scale;
+ final float minMagnifiedRegionCenterY = (viewportHeight / 2) / scale;
+ final float maxMagnifiedRegionCenterX = viewportWidth - minMagnifiedRegionCenterX;
+ final float maxMagnifiedRegionCenterY = viewportHeight - minMagnifiedRegionCenterY;
+
+ mMagnifiedRegionCenterX = Math.min(Math.max(magnifiedRegionCenterX,
+ minMagnifiedRegionCenterX), maxMagnifiedRegionCenterX);
+ mMagnifiedRegionCenterY = Math.min(Math.max(magnifiedRegionCenterY,
+ minMagnifiedRegionCenterY), maxMagnifiedRegionCenterY);
+
+ mScaledOffsetX = -(mMagnifiedRegionCenterX * scale - viewportWidth / 2);
+ mScaledOffsetY = -(mMagnifiedRegionCenterY * scale - viewportHeight / 2);
+ }
+
+ public void updateFrom(MagnificationSpec other) {
+ mScale = other.mScale;
+ mMagnifiedRegionCenterX = other.mMagnifiedRegionCenterX;
+ mMagnifiedRegionCenterY = other.mMagnifiedRegionCenterY;
+ mScaledOffsetX = other.mScaledOffsetX;
+ mScaledOffsetY = other.mScaledOffsetY;
+ }
+
+ public void reset() {
+ mScale = DEFAULT_SCALE;
+ mMagnifiedRegionCenterX = 0;
+ mMagnifiedRegionCenterY = 0;
+ mScaledOffsetX = 0;
+ mScaledOffsetY = 0;
+ }
+ }
+ }
+
+ private static final class Viewport {
+
+ private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+ private static final String PROPERTY_NAME_BOUNDS = "bounds";
+
+ private static final int MIN_ALPHA = 0;
+
+ private static final int MAX_ALPHA = 255;
+
+ private final ArrayList<WindowInfo> mTempWindowInfoList = new ArrayList<WindowInfo>();
+
+ private final Rect mTempRect = new Rect();
+
+ private final IWindowManager mWindowManagerService;
+ private final DisplayProvider mDisplayProvider;
+
+ private final ViewportWindow mViewportFrame;
+
+ private final ValueAnimator mResizeFrameAnimator;
+
+ private final ValueAnimator mShowHideFrameAnimator;
+
+ public Viewport(Context context, WindowManager windowManager,
+ IWindowManager windowManagerService, DisplayProvider displayInfoProvider,
+ Interpolator animationInterpolator, long animationDuration) {
+ mWindowManagerService = windowManagerService;
+ mDisplayProvider = displayInfoProvider;
+ mViewportFrame = new ViewportWindow(context, windowManager, displayInfoProvider);
+
+ mShowHideFrameAnimator = ObjectAnimator.ofInt(mViewportFrame, PROPERTY_NAME_ALPHA,
+ MIN_ALPHA, MAX_ALPHA);
+ mShowHideFrameAnimator.setInterpolator(animationInterpolator);
+ mShowHideFrameAnimator.setDuration(animationDuration);
+ mShowHideFrameAnimator.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mShowHideFrameAnimator.getAnimatedValue().equals(MIN_ALPHA)) {
+ mViewportFrame.hide();
+ }
+ }
+ @Override
+ public void onAnimationStart(Animator animation) {
+ /* do nothing - stub */
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ /* do nothing - stub */
+ }
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ /* do nothing - stub */
+ }
+ });
+
+ Property<ViewportWindow, Rect> property = Property.of(ViewportWindow.class,
+ Rect.class, PROPERTY_NAME_BOUNDS);
+ TypeEvaluator<Rect> evaluator = new TypeEvaluator<Rect>() {
+ private final Rect mReusableResultRect = new Rect();
+ @Override
+ public Rect evaluate(float fraction, Rect fromFrame, Rect toFrame) {
+ Rect result = mReusableResultRect;
+ result.left = (int) (fromFrame.left
+ + (toFrame.left - fromFrame.left) * fraction);
+ result.top = (int) (fromFrame.top
+ + (toFrame.top - fromFrame.top) * fraction);
+ result.right = (int) (fromFrame.right
+ + (toFrame.right - fromFrame.right) * fraction);
+ result.bottom = (int) (fromFrame.bottom
+ + (toFrame.bottom - fromFrame.bottom) * fraction);
+ return result;
+ }
+ };
+ mResizeFrameAnimator = ObjectAnimator.ofObject(mViewportFrame, property,
+ evaluator, mViewportFrame.mBounds, mViewportFrame.mBounds);
+ mResizeFrameAnimator.setDuration((long) (animationDuration));
+ mResizeFrameAnimator.setInterpolator(animationInterpolator);
+
+ recomputeBounds(false);
+ }
+
+ public void recomputeBounds(boolean animate) {
+ Rect frame = mTempRect;
+ frame.set(0, 0, mDisplayProvider.getDisplayInfo().logicalWidth,
+ mDisplayProvider.getDisplayInfo().logicalHeight);
+ ArrayList<WindowInfo> infos = mTempWindowInfoList;
+ infos.clear();
+ try {
+ mWindowManagerService.getVisibleWindowsForDisplay(
+ mDisplayProvider.getDisplay().getDisplayId(), infos);
+ final int windowCount = infos.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowInfo info = infos.get(i);
+ if (info.type == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD
+ || info.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG) {
+ subtract(frame, info.touchableRegion);
+ }
+ info.recycle();
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ } finally {
+ infos.clear();
+ }
+ resize(frame, animate);
+ }
+
+ public void rotationChanged() {
+ mViewportFrame.rotationChanged();
+ }
+
+ public Rect getBounds() {
+ return mViewportFrame.getBounds();
+ }
+
+ public void setFrameShown(boolean shown, boolean animate) {
+ if (mViewportFrame.isShown() == shown) {
+ return;
+ }
+ if (animate) {
+ if (mShowHideFrameAnimator.isRunning()) {
+ mShowHideFrameAnimator.reverse();
+ } else {
+ if (shown) {
+ mViewportFrame.show();
+ mShowHideFrameAnimator.start();
+ } else {
+ mShowHideFrameAnimator.reverse();
+ }
+ }
+ } else {
+ mShowHideFrameAnimator.cancel();
+ if (shown) {
+ mViewportFrame.show();
+ } else {
+ mViewportFrame.hide();
+ }
+ }
+ }
+
+ private void resize(Rect bounds, boolean animate) {
+ if (mViewportFrame.getBounds().equals(bounds)) {
+ return;
+ }
+ if (animate) {
+ if (mResizeFrameAnimator.isRunning()) {
+ mResizeFrameAnimator.cancel();
+ }
+ mResizeFrameAnimator.setObjectValues(mViewportFrame.mBounds, bounds);
+ mResizeFrameAnimator.start();
+ } else {
+ mViewportFrame.setBounds(bounds);
+ }
+ }
+
+ private boolean subtract(Rect lhs, Rect rhs) {
+ if (lhs.right < rhs.left || lhs.left > rhs.right
+ || lhs.bottom < rhs.top || lhs.top > rhs.bottom) {
+ return false;
+ }
+ if (lhs.left < rhs.left) {
+ lhs.right = rhs.left;
+ }
+ if (lhs.top < rhs.top) {
+ lhs.bottom = rhs.top;
+ }
+ if (lhs.right > rhs.right) {
+ lhs.left = rhs.right;
+ }
+ if (lhs.bottom > rhs.bottom) {
+ lhs.top = rhs.bottom;
+ }
+ return true;
+ }
+
+ private static final class ViewportWindow {
+ private static final String WINDOW_TITLE = "Magnification Overlay";
+
+ private final WindowManager mWindowManager;
+ private final DisplayProvider mDisplayProvider;
+
+ private final ContentView mWindowContent;
+ private final WindowManager.LayoutParams mWindowParams;
+
+ private final Rect mBounds = new Rect();
+ private boolean mShown;
+ private int mAlpha;
+
+ public ViewportWindow(Context context, WindowManager windowManager,
+ DisplayProvider displayProvider) {
+ mWindowManager = windowManager;
+ mDisplayProvider = displayProvider;
+
+ ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ mWindowContent = new ContentView(context);
+ mWindowContent.setLayoutParams(contentParams);
+ mWindowContent.setBackgroundColor(R.color.transparent);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams.setTitle(WINDOW_TITLE);
+ mWindowParams.gravity = Gravity.CENTER;
+ mWindowParams.width = displayProvider.getDisplayInfo().logicalWidth;
+ mWindowParams.height = displayProvider.getDisplayInfo().logicalHeight;
+ mWindowParams.format = PixelFormat.TRANSLUCENT;
+ }
+
+ public boolean isShown() {
+ return mShown;
+ }
+
+ public void show() {
+ if (mShown) {
+ return;
+ }
+ mShown = true;
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow shown.");
+ }
+ }
+
+ public void hide() {
+ if (!mShown) {
+ return;
+ }
+ mShown = false;
+ mWindowManager.removeView(mWindowContent);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow hidden.");
+ }
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ @SuppressWarnings("unused")
+ // Called reflectively from an animator.
+ public void setAlpha(int alpha) {
+ if (mAlpha == alpha) {
+ return;
+ }
+ mAlpha = alpha;
+ if (mShown) {
+ mWindowContent.invalidate();
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportFrame set alpha: " + alpha);
+ }
+ }
+
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ public void rotationChanged() {
+ mWindowParams.width = mDisplayProvider.getDisplayInfo().logicalWidth;
+ mWindowParams.height = mDisplayProvider.getDisplayInfo().logicalHeight;
+ if (mShown) {
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void setBounds(Rect bounds) {
+ if (mBounds.equals(bounds)) {
+ return;
+ }
+ mBounds.set(bounds);
+ if (mShown) {
+ mWindowContent.invalidate();
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportFrame set bounds: " + bounds);
+ }
+ }
+
+ private final class ContentView extends View {
+ private final Drawable mHighlightFrame;
+
+ public ContentView(Context context) {
+ super(context);
+ mHighlightFrame = context.getResources().getDrawable(
+ R.drawable.magnified_region_frame);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mHighlightFrame.setBounds(mBounds);
+ mHighlightFrame.setAlpha(mAlpha);
+ mHighlightFrame.draw(canvas);
+ }
+ }
+ }
+ }
+
+ private static class DisplayProvider implements DisplayListener {
+ private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ public DisplayProvider(Context context, WindowManager windowManager) {
+ mWindowManager = windowManager;
+ mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ mDisplayManager.registerDisplayListener(this, null);
+ updateDisplayInfo();
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDefaultDisplayInfo;
+ }
+
+ public Display getDisplay() {
+ return mDefaultDisplay;
+ }
+
+ private void updateDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.e(LOG_TAG, "Default display is not valid.");
+ }
+ }
+
+ public void destroy() {
+ mDisplayManager.unregisterDisplayListener(this);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ /* do noting */
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // Having no default display
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updateDisplayInfo();
+ }
+ }
+}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index ba9f2cd..9e4f33e 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -28,7 +28,6 @@
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
-import android.view.InputFilter;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -64,7 +63,7 @@
*
* @hide
*/
-public class TouchExplorer {
+class TouchExplorer implements EventStreamTransformation {
private static final boolean DEBUG = false;
@@ -120,10 +119,6 @@
// Slop between the first and second tap to be a double tap.
private final int mDoubleTapSlop;
- // The InputFilter this tracker is associated with i.e. the filter
- // which delegates event processing to this touch explorer.
- private final InputFilter mInputFilter;
-
// The current state of the touch explorer.
private int mCurrentState = STATE_TOUCH_EXPLORING;
@@ -155,6 +150,9 @@
// The scaled velocity above which we detect gestures.
private final int mScaledGestureDetectionVelocity;
+ // The handler to which to delegate events.
+ private EventStreamTransformation mNext;
+
// Helper to track gesture velocity.
private VelocityTracker mVelocityTracker;
@@ -206,12 +204,10 @@
* @param inputFilter The input filter associated with this explorer.
* @param context A context handle for accessing resources.
*/
- public TouchExplorer(InputFilter inputFilter, Context context,
- AccessibilityManagerService service) {
+ public TouchExplorer(Context context, AccessibilityManagerService service) {
mAms = service;
mReceivedPointerTracker = new ReceivedPointerTracker(context);
mInjectedPointerTracker = new InjectedPointerTracker();
- mInputFilter = inputFilter;
mTapTimeout = ViewConfiguration.getTapTimeout();
mDetermineUserIntentTimeout = (int) (mTapTimeout * 1.5f);
mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
@@ -242,7 +238,11 @@
}
}
- public void clear(MotionEvent event, int policyFlags) {
+ public void onDestroy() {
+ // TODO: Implement
+ }
+
+ private void clear(MotionEvent event, int policyFlags) {
switch (mCurrentState) {
case STATE_TOUCH_EXPLORING: {
// If a touch exploration gesture is in progress send events for its end.
@@ -278,8 +278,17 @@
mLongPressingPointerDeltaX = 0;
mLongPressingPointerDeltaY = 0;
mCurrentState = STATE_TOUCH_EXPLORING;
+ if (mNext != null) {
+ mNext.clear();
+ }
}
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mNext = next;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent event, int policyFlags) {
if (DEBUG) {
Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
@@ -325,6 +334,9 @@
mLastTouchedWindowId = event.getWindowId();
} break;
}
+ if (mNext != null) {
+ mNext.onAccessibilityEvent(event);
+ }
}
/**
@@ -958,7 +970,9 @@
// Make sure that the user will see the event.
policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- mInputFilter.sendInputEvent(event, policyFlags);
+ if (mNext != null) {
+ mNext.onMotionEvent(event, policyFlags);
+ }
mInjectedPointerTracker.onMotionEvent(event);
@@ -1008,11 +1022,13 @@
private MotionEvent mFirstTapEvent;
public void onMotionEvent(MotionEvent event, int policyFlags) {
+ final int actionIndex = event.getActionIndex();
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
- if (mFirstTapEvent != null && !isSamePointerContext(mFirstTapEvent, event)) {
+ if (mFirstTapEvent != null
+ && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) {
clear();
}
mDownEvent = MotionEvent.obtain(event);
@@ -1022,19 +1038,21 @@
if (mDownEvent == null) {
return;
}
- if (!isSamePointerContext(mDownEvent, event)) {
+ if (!GestureUtils.isSamePointerContext(mDownEvent, event)) {
clear();
return;
}
- if (isTap(mDownEvent, event)) {
- if (mFirstTapEvent == null || isTimedOut(mFirstTapEvent, event,
- mDoubleTapTimeout)) {
+ if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop,
+ actionIndex)) {
+ if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent,
+ event, mDoubleTapTimeout)) {
mFirstTapEvent = MotionEvent.obtain(event);
mDownEvent.recycle();
mDownEvent = null;
return;
}
- if (isDoubleTap(mFirstTapEvent, event)) {
+ if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout,
+ mDoubleTapSlop, actionIndex)) {
onDoubleTap(event, policyFlags);
mFirstTapEvent.recycle();
mFirstTapEvent = null;
@@ -1140,42 +1158,6 @@
}
}
- public boolean isTap(MotionEvent down, MotionEvent up) {
- return eventsWithinTimeoutAndDistance(down, up, mTapTimeout, mTouchSlop);
- }
-
- private boolean isDoubleTap(MotionEvent firstUp, MotionEvent secondUp) {
- return eventsWithinTimeoutAndDistance(firstUp, secondUp, mDoubleTapTimeout,
- mDoubleTapSlop);
- }
-
- private boolean eventsWithinTimeoutAndDistance(MotionEvent first, MotionEvent second,
- int timeout, int distance) {
- if (isTimedOut(first, second, timeout)) {
- return false;
- }
- final int downPtrIndex = first.getActionIndex();
- final int upPtrIndex = second.getActionIndex();
- final float deltaX = second.getX(upPtrIndex) - first.getX(downPtrIndex);
- final float deltaY = second.getY(upPtrIndex) - first.getY(downPtrIndex);
- final double deltaMove = Math.hypot(deltaX, deltaY);
- if (deltaMove >= distance) {
- return false;
- }
- return true;
- }
-
- private boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
- final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
- return (deltaTime >= timeout);
- }
-
- private boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
- return (first.getPointerIdBits() == second.getPointerIdBits()
- && first.getPointerId(first.getActionIndex())
- == second.getPointerId(second.getActionIndex()));
- }
-
public boolean firstTapDetected() {
return mFirstTapEvent != null
&& SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
@@ -1201,47 +1183,14 @@
final float secondPtrX = event.getX(secondPtrIndex);
final float secondPtrY = event.getY(secondPtrIndex);
- // Check if the pointers are moving in the same direction.
- final float firstDeltaX =
- firstPtrX - receivedTracker.getReceivedPointerDownX(firstPtrIndex);
- final float firstDeltaY =
- firstPtrY - receivedTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(firstPtrIndex);
+ final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(secondPtrIndex);
+ final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(secondPtrIndex);
- if (firstDeltaX == 0 && firstDeltaY == 0) {
- return true;
- }
-
- final float firstMagnitude =
- (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
- final float firstXNormalized =
- (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
- final float firstYNormalized =
- (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
-
- final float secondDeltaX =
- secondPtrX - receivedTracker.getReceivedPointerDownX(secondPtrIndex);
- final float secondDeltaY =
- secondPtrY - receivedTracker.getReceivedPointerDownY(secondPtrIndex);
-
- if (secondDeltaX == 0 && secondDeltaY == 0) {
- return true;
- }
-
- final float secondMagnitude =
- (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
- final float secondXNormalized =
- (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
- final float secondYNormalized =
- (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
-
- final float angleCos =
- firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
-
- if (angleCos < MAX_DRAGGING_ANGLE_COS) {
- return false;
- }
-
- return true;
+ return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
+ secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
+ MAX_DRAGGING_ANGLE_COS);
}
/**
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index ca7faa2..6a3010b 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -323,7 +323,7 @@
int startId) {
if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className
+ " " + token + " startId=" + startId);
- ServiceRecord r = findServiceLocked(className, token);
+ ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
if (startId >= 0) {
// Asked to only stop if done with all work. Note that
@@ -366,9 +366,10 @@
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, boolean removeNotification) {
+ final int userId = UserHandle.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
try {
- ServiceRecord r = findServiceLocked(className, token);
+ ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
if (id != 0) {
if (notification == null) {
@@ -427,9 +428,6 @@
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
- if (DEBUG_MU)
- Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
- + Binder.getOrigCallingUid());
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
@@ -677,8 +675,8 @@
}
private final ServiceRecord findServiceLocked(ComponentName name,
- IBinder token) {
- ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
+ IBinder token, int userId) {
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
return r == token ? r : null;
}
@@ -713,6 +711,9 @@
if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
+ userId = mAm.handleIncomingUserLocked(callingPid, callingUid, userId,
+ false, true, "service", null);
+
if (service.getComponent() != null) {
r = mServiceMap.getServiceByName(service.getComponent(), userId);
}
@@ -989,6 +990,17 @@
// restarting state.
mRestartingServices.remove(r);
+ // Make sure that the user who owns this service is started. If not,
+ // we don't want to allow it to run.
+ if (mAm.mStartedUsers.get(r.userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + r.appInfo.packageName + "/"
+ + r.appInfo.uid + " for service "
+ + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+ bringDownServiceLocked(r, true);
+ return false;
+ }
+
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
@@ -1410,9 +1422,6 @@
r.callStart = false;
}
}
- if (DEBUG_MU)
- Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
- + Binder.getOrigCallingUid());
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -1509,7 +1518,7 @@
boolean didSomething = false;
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
- if (service.packageName.equals(name)
+ if ((name == null || service.packageName.equals(name))
&& (service.app == null || evenPersistent || !service.app.persistent)) {
if (!doit) {
return true;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2b4f8b1..614b93a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -46,6 +46,7 @@
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
@@ -115,6 +116,7 @@
import android.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
+import android.util.LocaleUtil;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -152,7 +154,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -428,6 +429,11 @@
long mPreviousProcessVisibleTime;
/**
+ * Which uses have been started, so are allowed to run code.
+ */
+ final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+
+ /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -488,6 +494,15 @@
}
@Override
+ protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+ if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
+ || userId == filter.owningUserId) {
+ return super.newResult(filter, match, userId);
+ }
+ return null;
+ }
+
+ @Override
protected BroadcastFilter[] newArray(int size) {
return new BroadcastFilter[size];
}
@@ -499,12 +514,14 @@
};
/**
- * State of all active sticky broadcasts. Keys are the action of the
+ * State of all active sticky broadcasts per user. Keys are the action of the
* sticky Intent, values are an ArrayList of all broadcasted intents with
- * that action (which should usually be one).
+ * that action (which should usually be one). The SparseArray is keyed
+ * by the user ID the sticky is for, and can include UserHandle.USER_ALL
+ * for stickies that are sent to all users.
*/
- final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
- new HashMap<String, ArrayList<Intent>>();
+ final SparseArray<HashMap<String, ArrayList<Intent>>> mStickyBroadcasts =
+ new SparseArray<HashMap<String, ArrayList<Intent>>>();
final ActiveServices mServices;
@@ -791,7 +808,6 @@
static ActivityThread mSystemThread;
private int mCurrentUserId;
- private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
private UserManager mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1114,13 +1130,15 @@
notification.vibrate = null;
notification.setLatestEventInfo(context, text,
mContext.getText(R.string.heavy_weight_notification_detail),
- PendingIntent.getActivity(mContext, 0, root.intent,
- PendingIntent.FLAG_CANCEL_CURRENT));
+ PendingIntent.getActivityAsUser(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
try {
int[] outId = new int[1];
- inm.enqueueNotification("android", R.string.heavy_weight_notification,
- notification, outId);
+ inm.enqueueNotificationWithTag("android", null,
+ R.string.heavy_weight_notification,
+ notification, outId, root.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for heavy-weight app", e);
@@ -1136,8 +1154,8 @@
return;
}
try {
- inm.cancelNotification("android",
- R.string.heavy_weight_notification);
+ inm.cancelNotificationWithTag("android", null,
+ R.string.heavy_weight_notification, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
@@ -1506,11 +1524,15 @@
systemDir, "usagestats").toString());
mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
+ // User 0 is the first and only user that runs at boot.
+ mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
mConfiguration.setToDefaults();
- mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLocale(Locale.getDefault());
+
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
@@ -1983,13 +2005,19 @@
try {
final PackageManager pm = mContext.getPackageManager();
gids = pm.getPackageGids(app.info.packageName);
+
+ if (Environment.isExternalStorageEmulated()) {
+ if (pm.checkPermission(
+ android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
+ app.info.packageName) == PERMISSION_GRANTED) {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+ } else {
+ mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+ }
+ }
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
-
- if (Environment.isExternalStorageEmulated()) {
- mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
- }
}
if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -2095,7 +2123,7 @@
}
}
- boolean startHomeActivityLocked(int userId) {
+ boolean startHomeActivityLocked(int userId, UserStartedState startingUser) {
if (mHeadless) {
// Added because none of the other calls to ensureBootCompleted seem to fire
// when running headless.
@@ -2135,6 +2163,9 @@
null, null, 0, 0, 0, 0, null, false, null);
}
}
+ if (startingUser != null) {
+ mMainStack.addStartingUserLocked(startingUser);
+ }
return true;
}
@@ -2336,38 +2367,14 @@
String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
if (userId != UserHandle.getCallingUserId()) {
- // Requesting a different user, make sure that they have the permission
- if (checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
- == PackageManager.PERMISSION_GRANTED) {
- // Translate to the current user id, if caller wasn't aware
- if (userId == UserHandle.USER_CURRENT) {
- userId = mCurrentUserId;
- }
- } else {
- String msg = "Permission Denial: "
- + "Request to startActivity as user " + userId
- + " but is calling from user " + UserHandle.getCallingUserId()
- + "; this requires "
- + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivity", null);
} else {
if (intent.getCategories() != null
&& intent.getCategories().contains(Intent.CATEGORY_HOME)) {
// Requesting home, set the identity to the current user
// HACK!
userId = mCurrentUserId;
- } else {
- // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
- // the current user's userId
- if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
- userId = 0;
- } else {
- userId = Binder.getOrigCallingUser();
- }
}
}
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -2381,21 +2388,22 @@
ParcelFileDescriptor profileFd, Bundle options) {
enforceNotIsolatedCaller("startActivityAndWait");
WaitResult res = new WaitResult();
- int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- res, null, options, userId);
+ res, null, options, UserHandle.getCallingUserId());
return res;
}
public final int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Configuration config,
- Bundle options) {
+ Bundle options, int userId) {
enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityWithConfig", null);
int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, config, options, Binder.getOrigCallingUser());
+ null, null, null, config, options, userId);
return ret;
}
@@ -2526,21 +2534,16 @@
}
}
- public final int startActivityInPackage(int uid,
+ final int startActivityInPackage(int uid,
Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options) {
-
- // This is so super not safe, that only the system (or okay root)
- // can do it.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- throw new SecurityException(
- "startActivityInPackage only available to the system");
- }
+ String resultWho, int requestCode, int startFlags, Bundle options, int userId) {
+
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, null, options, UserHandle.getUserId(uid));
+ null, null, null, null, options, userId);
return ret;
}
@@ -2548,23 +2551,18 @@
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options) {
enforceNotIsolatedCaller("startActivities");
int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
- options, Binder.getOrigCallingUser());
+ options, UserHandle.getCallingUserId());
return ret;
}
- public final int startActivitiesInPackage(int uid,
+ final int startActivitiesInPackage(int uid,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) {
+ Bundle options, int userId) {
- // This is so super not safe, that only the system (or okay root)
- // can do it.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- throw new SecurityException(
- "startActivityInPackage only available to the system");
- }
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
- options, UserHandle.getUserId(uid));
+ options, userId);
return ret;
}
@@ -2698,8 +2696,9 @@
}
}
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
}
@@ -3454,7 +3453,7 @@
Slog.w(TAG, "Invalid packageName: " + packageName);
return;
}
- killPackageProcessesLocked(packageName, pkgUid,
+ killPackageProcessesLocked(packageName, pkgUid, -1,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
} finally {
@@ -3573,22 +3572,39 @@
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
+ final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
- synchronized (this) {
- closeSystemDialogsLocked(uid, reason);
+ try {
+ synchronized (this) {
+ // Only allow this from foreground processes, so that background
+ // applications can't abuse it to prevent system UI from being shown.
+ if (uid >= Process.FIRST_APPLICATION_UID) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ + " from background process " + proc);
+ return;
+ }
+ }
+ closeSystemDialogsLocked(reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
- void closeSystemDialogsLocked(int callingUid, String reason) {
+ void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
if (reason != null) {
intent.putExtra("reason", reason);
}
mWindowManager.closeSystemDialogs(reason);
-
+
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
@@ -3596,10 +3612,10 @@
Activity.RESULT_CANCELED, null, "close-sys");
}
}
-
+
broadcastIntentLocked(null, null, intent, null,
null, 0, null, null, null, false, false, -1,
- callingUid, 0 /* TODO: Verify */);
+ Process.SYSTEM_UID, UserHandle.USER_ALL);
}
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
@@ -3650,7 +3666,8 @@
}
private void forceStopPackageLocked(final String packageName, int uid) {
- forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
+ forceStopPackageLocked(packageName, uid, false, false, true, false,
+ UserHandle.getUserId(uid));
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -3662,16 +3679,27 @@
false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
-
+
+ private void forceStopUserLocked(int userId) {
+ forceStopPackageLocked(null, -1, false, false, true, false, userId);
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false,
+ MY_PID, Process.SYSTEM_UID, userId);
+ }
+
private final boolean killPackageProcessesLocked(String packageName, int uid,
- int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit,
- boolean evenPersistent, String reason) {
+ int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+ boolean doit, boolean evenPersistent, String reason) {
ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
// matches the package name.
- final String procNamePrefix = packageName + ":";
+ final String procNamePrefix = packageName != null ? (packageName + ":") : null;
for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
@@ -3684,6 +3712,18 @@
if (doit) {
procs.add(app);
}
+ // If no package is specified, we call all processes under the
+ // give user id.
+ } else if (packageName == null) {
+ if (app.userId == userId) {
+ if (app.setAdj >= minOomAdj) {
+ if (!doit) {
+ return true;
+ }
+ app.removed = true;
+ procs.add(app);
+ }
+ }
// If uid is specified and the uid and process name match
// Or, the uid is not specified and the process name matches
} else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
@@ -3714,7 +3754,7 @@
int i;
int N;
- if (uid < 0) {
+ if (uid < 0 && name != null) {
try {
uid = AppGlobals.getPackageManager().getPackageUid(name, userId);
} catch (RemoteException e) {
@@ -3722,24 +3762,45 @@
}
if (doit) {
- Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+ if (name != null) {
+ Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+ } else {
+ Slog.i(TAG, "Force stopping user " + userId);
+ }
Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
while (badApps.hasNext()) {
SparseArray<Long> ba = badApps.next();
- if (ba.get(uid) != null) {
+ for (i=ba.size()-1; i>=0; i--) {
+ boolean remove = false;
+ final int entUid = ba.keyAt(i);
+ if (name != null) {
+ if (entUid == uid) {
+ remove = true;
+ }
+ } else if (UserHandle.getUserId(entUid) == userId) {
+ remove = true;
+ }
+ if (remove) {
+ ba.removeAt(i);
+ }
+ }
+ if (ba.size() == 0) {
badApps.remove();
}
}
}
-
- boolean didSomething = killPackageProcessesLocked(name, uid, -100,
- callerWillRestart, false, doit, evenPersistent, "force stop");
+
+ boolean didSomething = killPackageProcessesLocked(name, uid,
+ name == null ? userId : -1 , -100, callerWillRestart, false,
+ doit, evenPersistent,
+ name == null ? ("force stop user " + userId) : ("force stop " + name));
TaskRecord lastTask = null;
for (i=0; i<mMainStack.mHistory.size(); i++) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
- final boolean samePackage = r.packageName.equals(name);
+ final boolean samePackage = r.packageName.equals(name)
+ || (name == null && r.userId == userId);
if (r.userId == userId
&& (samePackage || r.task == lastTask)
&& (r.app == null || evenPersistent || !r.app.persistent)) {
@@ -3774,9 +3835,14 @@
didSomething = true;
}
+ if (name == null) {
+ // Remove all sticky broadcasts from this user.
+ mStickyBroadcasts.remove(userId);
+ }
+
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
- if (provider.info.packageName.equals(name)
+ if ((name == null || provider.info.packageName.equals(name))
&& (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
if (!doit) {
return true;
@@ -3792,7 +3858,7 @@
}
if (doit) {
- if (purgeCache) {
+ if (purgeCache && name != null) {
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(name);
@@ -3818,8 +3884,9 @@
mProcessNames.remove(name, uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
@@ -3865,8 +3932,9 @@
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
@@ -4197,15 +4265,6 @@
}
}, pkgFilter);
- IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onUserRemoved(intent);
- }
- }, userFilter);
-
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
@@ -4227,13 +4286,18 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- List<UserInfo> users = getUserManager().getUsers();
- for (UserInfo user : users) {
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID, user.id);
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ if (uss.mState == UserStartedState.STATE_BOOTING) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ final int userId = mStartedUsers.keyAt(i);
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
}
}
}
@@ -4344,7 +4408,7 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options) {
+ int flags, Bundle options, int userId) {
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -4378,6 +4442,8 @@
synchronized(this) {
int callingUid = Binder.getCallingUid();
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), callingUid, userId,
+ false, true, "getIntentSender", null);
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
@@ -4392,11 +4458,8 @@
throw new SecurityException(msg);
}
}
-
- if (DEBUG_MU)
- Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
- + Binder.getOrigCallingUid());
- return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
+
+ return getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);
} catch (RemoteException e) {
@@ -4405,8 +4468,8 @@
}
}
- IIntentSender getIntentSenderLocked(int type,
- String packageName, int callingUid, IBinder token, String resultWho,
+ IIntentSender getIntentSenderLocked(int type, String packageName,
+ int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options) {
if (DEBUG_MU)
@@ -4430,7 +4493,7 @@
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intents, resolvedTypes, flags, options);
+ requestCode, intents, resolvedTypes, flags, options, userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
@@ -6171,7 +6234,7 @@
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, boolean stable) {
+ String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -6186,10 +6249,13 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
+ if (r.userId != userId) {
+ throw new SecurityException("Calling requested user " + userId
+ + " but app is user " + r.userId);
+ }
}
// First check if this content provider has been published...
- int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid());
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
@@ -6296,6 +6362,16 @@
"Attempt to launch content provider before system ready");
}
+ // Make sure that the user who owns this provider is started. If not,
+ // we don't want to allow it to run.
+ if (mStartedUsers.get(userId) == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": user " + userId + " is stopped");
+ return null;
+ }
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
final boolean firstClass = cpr == null;
@@ -6434,17 +6510,19 @@
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name, null, stable);
+ return getContentProviderImpl(caller, name, null, stable,
+ UserHandle.getCallingUserId());
}
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
- return getContentProviderExternalUnchecked(name, token);
+ return getContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
- return getContentProviderImpl(null, name, token, true);
+ private ContentProviderHolder getContentProviderExternalUnchecked(String name,
+ IBinder token, int userId) {
+ return getContentProviderImpl(null, name, token, true, userId);
}
/**
@@ -6475,13 +6553,12 @@
public void removeContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- removeContentProviderExternalUnchecked(name, token);
+ removeContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private void removeContentProviderExternalUnchecked(String name, IBinder token) {
+ private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
synchronized (this) {
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- Binder.getOrigCallingUser());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6490,8 +6567,7 @@
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
- Binder.getOrigCallingUser());
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
if (localCpr.hasExternalProcessHandles()) {
if (localCpr.removeExternalProcessHandleLocked(token)) {
updateOomAdjLocked();
@@ -6705,11 +6781,12 @@
public String getProviderMimeType(Uri uri) {
enforceNotIsolatedCaller("getProviderMimeType");
final String name = uri.getAuthority();
+ final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
ContentProviderHolder holder = null;
try {
- holder = getContentProviderExternalUnchecked(name, null);
+ holder = getContentProviderExternalUnchecked(name, null, userId);
if (holder != null) {
return holder.provider.getType(uri);
}
@@ -6718,7 +6795,7 @@
return null;
} finally {
if (holder != null) {
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
}
Binder.restoreCallingIdentity(ident);
}
@@ -6822,8 +6899,9 @@
public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
+ final int userId = UserHandle.getCallingUserId();
String name = uri.getAuthority();
- ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null);
+ ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
@@ -6845,7 +6923,7 @@
}
// We've got the fd now, so we're done with the provider.
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
}
@@ -7485,7 +7563,7 @@
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky) {
+ boolean sticky, int sendingUser) {
// The raw IIntentReceiver interface is called
// with the AM lock held, so redispatch to
// execute our code without the lock.
@@ -7505,10 +7583,10 @@
};
}
Slog.i(TAG, "Sending system update to: " + intent.getComponent());
- /* TODO: Send this to all users */
+ // XXX also need to send this to stopped users(!!!)
broadcastIntentLocked(null, null, intent, null, finisher,
0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
- 0 /* UserId zero */);
+ UserHandle.USER_ALL);
if (finisher != null) {
mWaitingUpdate = true;
}
@@ -8121,7 +8199,7 @@
for (String pkg : process.pkgList) {
sb.append("Package: ").append(pkg);
try {
- PackageInfo pi = pm.getPackageInfo(pkg, 0, 0);
+ PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
if (pi != null) {
sb.append(" v").append(pi.versionCode);
if (pi.versionName != null) {
@@ -9047,6 +9125,13 @@
}
pw.println();
+ pw.println(" mStartedUsers:");
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.println(":");
+ uss.dump(" ", pw);
+ }
pw.println(" mHomeProcess: " + mHomeProcess);
pw.println(" mPreviousProcess: " + mPreviousProcess);
if (dumpAll) {
@@ -9404,9 +9489,15 @@
boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
-
+ boolean onlyHistory = false;
+
+ if ("history".equals(dumpPackage)) {
+ onlyHistory = true;
+ dumpPackage = null;
+ }
+
pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
- if (dumpAll) {
+ if (!onlyHistory && dumpAll) {
if (mRegisteredReceivers.size() > 0) {
boolean printed = false;
Iterator it = mRegisteredReceivers.values().iterator();
@@ -9439,39 +9530,41 @@
needSep = true;
- if (mStickyBroadcasts != null && dumpPackage == null) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Sticky broadcasts:");
- StringBuilder sb = new StringBuilder(128);
- for (Map.Entry<String, ArrayList<Intent>> ent
- : mStickyBroadcasts.entrySet()) {
- pw.print(" * Sticky action "); pw.print(ent.getKey());
- if (dumpAll) {
- pw.println(":");
- ArrayList<Intent> intents = ent.getValue();
- final int N = intents.size();
- for (int i=0; i<N; i++) {
- sb.setLength(0);
- sb.append(" Intent: ");
- intents.get(i).toShortString(sb, false, true, false, false);
- pw.println(sb.toString());
- Bundle bundle = intents.get(i).getExtras();
- if (bundle != null) {
- pw.print(" ");
- pw.println(bundle.toString());
+ if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) {
+ for (int user=0; user<mStickyBroadcasts.size(); user++) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.print(" Sticky broadcasts for user ");
+ pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":");
+ StringBuilder sb = new StringBuilder(128);
+ for (Map.Entry<String, ArrayList<Intent>> ent
+ : mStickyBroadcasts.valueAt(user).entrySet()) {
+ pw.print(" * Sticky action "); pw.print(ent.getKey());
+ if (dumpAll) {
+ pw.println(":");
+ ArrayList<Intent> intents = ent.getValue();
+ final int N = intents.size();
+ for (int i=0; i<N; i++) {
+ sb.setLength(0);
+ sb.append(" Intent: ");
+ intents.get(i).toShortString(sb, false, true, false, false);
+ pw.println(sb.toString());
+ Bundle bundle = intents.get(i).getExtras();
+ if (bundle != null) {
+ pw.print(" ");
+ pw.println(bundle.toString());
+ }
}
+ } else {
+ pw.println("");
}
- } else {
- pw.println("");
}
}
- needSep = true;
}
- if (dumpAll) {
+ if (!onlyHistory && dumpAll) {
pw.println();
for (BroadcastQueue queue : mBroadcastQueues) {
pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]="
@@ -10475,8 +10568,9 @@
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
@@ -10527,7 +10621,9 @@
restart = true;
} else {
removeDyingProviderLocked(app, cpr, true);
+ // cpr should have been removed from mLaunchingProviders
NL = mLaunchingProviders.size();
+ i--;
}
}
}
@@ -10576,13 +10672,13 @@
}
ComponentName startServiceInPackage(int uid,
- Intent service, String resolvedType) {
+ Intent service, String resolvedType, int userId) {
synchronized(this) {
if (DEBUG_SERVICE)
Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, UserHandle.getUserId(uid));
+ resolvedType, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -10629,6 +10725,66 @@
}
}
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) {
+ synchronized(this) {
+ return handleIncomingUserLocked(callingPid, callingUid, userId, allowAll,
+ requireFull, name, callerPackage);
+ }
+ }
+
+ int handleIncomingUserLocked(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (callingUserId != userId) {
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ if ((requireFull || checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED)
+ && checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid, callingUid, -1, true)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+ // In this case, they would like to just execute as their
+ // owner user instead of failing.
+ userId = callingUserId;
+ } else {
+ StringBuilder builder = new StringBuilder(128);
+ builder.append("Permission Denial: ");
+ builder.append(name);
+ if (callerPackage != null) {
+ builder.append(" from ");
+ builder.append(callerPackage);
+ }
+ builder.append(" asks to run as user ");
+ builder.append(userId);
+ builder.append(" but is calling from user ");
+ builder.append(UserHandle.getUserId(callingUid));
+ builder.append("; this requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (!requireFull) {
+ builder.append("or");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+ String msg = builder.toString();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+ }
+ if (userId == UserHandle.USER_CURRENT
+ || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ userId = mCurrentUserId;
+ }
+ if (!allowAll && userId < 0) {
+ throw new IllegalArgumentException(
+ "Call does not support special user #" + userId);
+ }
+ }
+ return userId;
+ }
+
boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
String className, int flags) {
boolean result = false;
@@ -10667,8 +10823,6 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- checkValidCaller(Binder.getCallingUid(), userId);
-
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service, resolvedType,
connection, flags, userId);
@@ -10855,9 +11009,13 @@
// =========================================================
private final List getStickiesLocked(String action, IntentFilter filter,
- List cur) {
+ List cur, int userId) {
final ContentResolver resolver = mContext.getContentResolver();
- final ArrayList<Intent> list = mStickyBroadcasts.get(action);
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies == null) {
+ return cur;
+ }
+ final ArrayList<Intent> list = stickies.get(action);
if (list == null) {
return cur;
}
@@ -10896,9 +11054,10 @@
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
- IIntentReceiver receiver, IntentFilter filter, String permission) {
+ IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid;
+ int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
@@ -10915,11 +11074,16 @@
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
+ callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
}
+ userId = this.handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, true, "registerReceiver", callerPackage);
+
List allSticky = null;
// Look for any matching sticky broadcasts...
@@ -10927,10 +11091,16 @@
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
- allSticky = getStickiesLocked(action, filter, allSticky);
+ allSticky = getStickiesLocked(action, filter, allSticky,
+ UserHandle.USER_ALL);
+ allSticky = getStickiesLocked(action, filter, allSticky,
+ UserHandle.getUserId(callingUid));
}
} else {
- allSticky = getStickiesLocked(null, filter, allSticky);
+ allSticky = getStickiesLocked(null, filter, allSticky,
+ UserHandle.USER_ALL);
+ allSticky = getStickiesLocked(null, filter, allSticky,
+ UserHandle.getUserId(callingUid));
}
// The first sticky in the list is returned directly back to
@@ -10947,9 +11117,8 @@
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
- rl = new ReceiverList(this, callerApp,
- Binder.getCallingPid(),
- Binder.getCallingUid(), receiver);
+ rl = new ReceiverList(this, callerApp, callingPid, callingUid,
+ userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
@@ -10961,9 +11130,21 @@
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
+ } else if (rl.uid != callingUid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for uid " + callingUid
+ + " was previously registered for uid " + rl.uid);
+ } else if (rl.pid != callingPid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for pid " + callingPid
+ + " was previously registered for pid " + rl.pid);
+ } else if (rl.userId != userId) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for user " + userId
+ + " was previously registered for user " + rl.userId);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
- permission, callingUid);
+ permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -10982,7 +11163,7 @@
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, receivers, null, 0, null, null,
- false, true, true, false);
+ false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
@@ -11045,10 +11226,10 @@
}
}
- private final void sendPackageBroadcastLocked(int cmd, String[] packages) {
+ private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null) {
+ if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
try {
r.thread.dispatchPackageBroadcast(cmd, packages);
} catch (RemoteException ex) {
@@ -11056,7 +11237,67 @@
}
}
}
-
+
+ private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
+ int[] users) {
+ List<ResolveInfo> receivers = null;
+ try {
+ HashSet<ComponentName> singleUserReceivers = null;
+ boolean scannedFirstReceivers = false;
+ for (int user : users) {
+ List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
+ .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
+ if (newReceivers != null && newReceivers.size() == 0) {
+ newReceivers = null;
+ }
+ if (receivers == null) {
+ receivers = newReceivers;
+ } else if (newReceivers != null) {
+ // We need to concatenate the additional receivers
+ // found with what we have do far. This would be easy,
+ // but we also need to de-dup any receivers that are
+ // singleUser.
+ if (!scannedFirstReceivers) {
+ // Collect any single user receivers we had already retrieved.
+ scannedFirstReceivers = true;
+ for (int i=0; i<receivers.size(); i++) {
+ ResolveInfo ri = receivers.get(i);
+ if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ ComponentName cn = new ComponentName(
+ ri.activityInfo.packageName, ri.activityInfo.name);
+ if (singleUserReceivers == null) {
+ singleUserReceivers = new HashSet<ComponentName>();
+ }
+ singleUserReceivers.add(cn);
+ }
+ }
+ }
+ // Add the new results to the existing results, tracking
+ // and de-dupping single user receivers.
+ for (int i=0; i<newReceivers.size(); i++) {
+ ResolveInfo ri = receivers.get(i);
+ if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ ComponentName cn = new ComponentName(
+ ri.activityInfo.packageName, ri.activityInfo.name);
+ if (singleUserReceivers == null) {
+ singleUserReceivers = new HashSet<ComponentName>();
+ }
+ if (!singleUserReceivers.contains(cn)) {
+ singleUserReceivers.add(cn);
+ receivers.add(ri);
+ }
+ } else {
+ receivers.add(ri);
+ }
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ // pm is in same process, this will never happen.
+ }
+ return receivers;
+ }
+
private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
@@ -11075,30 +11316,38 @@
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
- boolean onlySendToCaller = false;
+ userId = handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, false, "broadcast", callerPackage);
- // If the caller is trying to send this broadcast to a different
- // user, verify that is allowed.
- if (UserHandle.getUserId(callingUid) != userId) {
- if (checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- callingPid, callingUid, -1, true)
- != PackageManager.PERMISSION_GRANTED) {
- if (checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- callingPid, callingUid, -1, true)
- == PackageManager.PERMISSION_GRANTED) {
- onlySendToCaller = true;
- } else {
- String msg = "Permission Denial: " + intent.getAction()
- + " broadcast from " + callerPackage
- + " asks to send as user " + userId
- + " but is calling from user " + UserHandle.getUserId(callingUid)
- + "; this requires "
- + android.Manifest.permission.INTERACT_ACROSS_USERS;
+ // Make sure that the user who is receiving this broadcast is started
+ // If not, we will just skip it.
+ if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
+ Slog.w(TAG, "Skipping broadcast of " + intent
+ + ": user " + userId + " is stopped");
+ return ActivityManager.BROADCAST_SUCCESS;
+ }
+
+ /*
+ * Prevent non-system code (defined here to be non-persistent
+ * processes) from sending protected broadcasts.
+ */
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
+ || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
+ callingUid == 0) {
+ // Always okay.
+ } else if (callerApp == null || !callerApp.persistent) {
+ try {
+ if (AppGlobals.getPackageManager().isProtectedBroadcast(
+ intent.getAction())) {
+ String msg = "Permission Denial: not allowed to send broadcast "
+ + intent.getAction() + " from pid="
+ + callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception", e);
+ return ActivityManager.BROADCAST_SUCCESS;
}
}
@@ -11126,7 +11375,7 @@
}
}
} else {
- // If resources are unvailble just force stop all
+ // If resources are unavailable just force stop all
// those packages and flush the attribute cache as well.
if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -11135,7 +11384,7 @@
forceStopPackageLocked(pkg, -1, false, true, true, false, userId);
}
sendPackageBroadcastLocked(
- IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list);
+ IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId);
}
} else {
Uri data = intent.getData();
@@ -11148,7 +11397,7 @@
}
if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
- new String[] {ssp});
+ new String[] {ssp}, userId);
}
}
}
@@ -11192,30 +11441,6 @@
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
}
- /*
- * Prevent non-system code (defined here to be non-persistent
- * processes) from sending protected broadcasts.
- */
- if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
- || callingUid == Process.SHELL_UID || callingUid == Process.BLUETOOTH_UID ||
- callingUid == 0) {
- // Always okay.
- } else if (callerApp == null || !callerApp.persistent) {
- try {
- if (AppGlobals.getPackageManager().isProtectedBroadcast(
- intent.getAction())) {
- String msg = "Permission Denial: not allowed to send broadcast "
- + intent.getAction() + " from pid="
- + callingPid + ", uid=" + callingUid;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Remote exception", e);
- return ActivityManager.BROADCAST_SUCCESS;
- }
- }
-
// Add to the sticky list if requested.
if (sticky) {
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
@@ -11236,10 +11461,38 @@
throw new SecurityException(
"Sticky broadcasts can't target a specific component");
}
- ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
+ // We use userId directly here, since the "all" target is maintained
+ // as a separate set of sticky broadcasts.
+ if (userId != UserHandle.USER_ALL) {
+ // But first, if this is not a broadcast to all users, then
+ // make sure it doesn't conflict with an existing broadcast to
+ // all users.
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
+ UserHandle.USER_ALL);
+ if (stickies != null) {
+ ArrayList<Intent> list = stickies.get(intent.getAction());
+ if (list != null) {
+ int N = list.size();
+ int i;
+ for (i=0; i<N; i++) {
+ if (intent.filterEquals(list.get(i))) {
+ throw new IllegalArgumentException(
+ "Sticky broadcast " + intent + " for user "
+ + userId + " conflicts with existing global broadcast");
+ }
+ }
+ }
+ }
+ }
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies == null) {
+ stickies = new HashMap<String, ArrayList<Intent>>();
+ mStickyBroadcasts.put(userId, stickies);
+ }
+ ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<Intent>();
- mStickyBroadcasts.put(intent.getAction(), list);
+ stickies.put(intent.getAction(), list);
}
int N = list.size();
int i;
@@ -11255,22 +11508,29 @@
}
}
+ int[] users;
+ if (userId == UserHandle.USER_ALL) {
+ // Caller wants broadcast to go to all started users.
+ users = new int[mStartedUsers.size()];
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ users[i] = mStartedUsers.keyAt(i);
+ }
+ } else {
+ // Caller wants broadcast to go to one specific user.
+ users = new int[] {userId};
+ }
+
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
- try {
- // Need to resolve the intent to interested receivers...
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- == 0) {
- receivers = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, resolvedType, STOCK_PM_FLAGS, userId);
- }
- if (intent.getComponent() == null) {
- registeredReceivers = mReceiverResolver.queryIntent(intent,
- resolvedType, false, userId);
- }
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
+ // Need to resolve the intent to interested receivers...
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ == 0) {
+ receivers = collectReceiverComponents(intent, resolvedType, users);
+ }
+ if (intent.getComponent() == null) {
+ registeredReceivers = mReceiverResolver.queryIntent(intent,
+ resolvedType, false, userId);
}
final boolean replacePending =
@@ -11288,7 +11548,7 @@
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
registeredReceivers, resultTo, resultCode, resultData, map,
- ordered, sticky, false, onlySendToCaller);
+ ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
@@ -11378,7 +11638,7 @@
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, requiredPermission,
receivers, resultTo, resultCode, resultData, map, ordered,
- sticky, false, onlySendToCaller);
+ sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
@@ -11463,13 +11723,15 @@
}
}
- // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ userId = handleIncomingUserLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, false, "removeStickyBroadcast", null);
+
synchronized(this) {
if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
!= PackageManager.PERMISSION_GRANTED) {
@@ -11480,15 +11742,24 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
- if (list != null) {
- int N = list.size();
- int i;
- for (i=0; i<N; i++) {
- if (intent.filterEquals(list.get(i))) {
- list.remove(i);
- break;
+ HashMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
+ if (stickies != null) {
+ ArrayList<Intent> list = stickies.get(intent.getAction());
+ if (list != null) {
+ int N = list.size();
+ int i;
+ for (i=0; i<N; i++) {
+ if (intent.filterEquals(list.get(i))) {
+ list.remove(i);
+ break;
+ }
}
+ if (list.size() <= 0) {
+ stickies.remove(intent.getAction());
+ }
+ }
+ if (stickies.size() <= 0) {
+ mStickyBroadcasts.remove(userId);
}
}
}
@@ -11644,7 +11915,7 @@
app.instrumentationProfileFile = null;
app.instrumentationArguments = null;
- forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId);
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId);
}
public void finishInstrumentation(IApplicationThread target,
@@ -11819,12 +12090,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+ null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_LOCALE_CHANGED),
null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+ null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
}
}
@@ -13486,73 +13757,175 @@
// Multi-user methods
+ @Override
public boolean switchUser(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to switch user from unauthorized app");
- return false;
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
- if (mCurrentUserId == userId)
- return true;
-
synchronized (this) {
- // Check if user is already logged in, otherwise check if user exists first before
- // adding to the list of logged in users.
- if (mLoggedInUsers.indexOfKey(userId) < 0) {
- if (!userExists(userId)) {
- return false;
- }
- mLoggedInUsers.append(userId, userId);
+ if (mCurrentUserId == userId) {
+ return true;
+ }
+
+ // 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 UserStartedState(new UserHandle(userId), false));
}
mCurrentUserId = userId;
boolean haveActivities = mMainStack.switchUser(userId);
if (!haveActivities) {
- startHomeActivityLocked(userId);
+ startHomeActivityLocked(userId, mStartedUsers.get(userId));
}
-
}
- // Inform of user switch
- Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Inform of user switch
+ Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
return true;
}
- @Override
- public UserInfo getCurrentUser() throws RemoteException {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- Slog.e(TAG, "Trying to get user from unauthorized app");
- return null;
+ void finishUserSwitch(UserStartedState uss) {
+ synchronized (this) {
+ if (uss.mState == UserStartedState.STATE_BOOTING
+ && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+ uss.mState = UserStartedState.STATE_RUNNING;
+ final int userId = uss.mHandle.getIdentifier();
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
}
- return getUserManager().getUserInfo(mCurrentUserId);
}
- private void onUserRemoved(Intent intent) {
- int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (extraUserId < 1) return;
-
- // Kill all the processes for the user
- ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+ @Override
+ public int stopUser(final int userId, final IStopUserCallback callback) {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (userId <= 0) {
+ throw new IllegalArgumentException("Can't stop primary user " + userId);
+ }
synchronized (this) {
- HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
- for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
- SparseArray<ProcessRecord> uids = uidMap.getValue();
- for (int i = 0; i < uids.size(); i++) {
- if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
- pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
- }
+ if (mCurrentUserId == userId) {
+ return ActivityManager.USER_OP_IS_CURRENT;
+ }
+
+ final UserStartedState uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ // User is not started, nothing to do... but we do need to
+ // callback if requested.
+ if (callback != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+ return ActivityManager.USER_OP_SUCCESS;
+ }
+
+ if (callback != null) {
+ uss.mStopCallbacks.add(callback);
+ }
+
+ if (uss.mState != UserStartedState.STATE_STOPPING) {
+ uss.mState = UserStartedState.STATE_STOPPING;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // Inform of user switch
+ Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
+ final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ finishUserStop(uss);
+ }
+ };
+ broadcastIntentLocked(null, null, intent,
+ null, resultReceiver, 0, null, null, null,
+ true, false, MY_PID, Process.SYSTEM_UID, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
+ }
- for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
- forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
- false, false, true, true, extraUserId);
+ return ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishUserStop(UserStartedState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ boolean stopped;
+ ArrayList<IStopUserCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ || mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else {
+ stopped = true;
+ // User can no longer run.
+ mStartedUsers.remove(userId);
+
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUserLocked(userId);
}
}
+
+ for (int i=0; i<callbacks.size(); i++) {
+ try {
+ if (stopped) callbacks.get(i).userStopped(userId);
+ else callbacks.get(i).userStopAborted(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public UserInfo getCurrentUser() {
+ if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: getCurrentUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (this) {
+ return getUserManager().getUserInfo(mCurrentUserId);
+ }
}
private boolean userExists(int userId) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index ccea41a..399ea59 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -211,7 +211,10 @@
*/
final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
= new ArrayList<IActivityManager.WaitResult>();
-
+
+ final ArrayList<UserStartedState> mStartingUsers
+ = new ArrayList<UserStartedState>();
+
/**
* Set when the system is going to sleep, until we have
* successfully paused the current activity and released our wake lock.
@@ -1397,7 +1400,7 @@
// Launcher...
if (mMainStack) {
ActivityOptions.abort(options);
- return mService.startHomeActivityLocked(0);
+ return mService.startHomeActivityLocked(0, null);
}
}
@@ -1427,7 +1430,16 @@
ActivityOptions.abort(options);
return false;
}
-
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (mService.mStartedUsers.get(next.userId) == null) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.userId + " is stopped");
+ return false;
+ }
+
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStoppingActivities.remove(next);
@@ -1494,7 +1506,7 @@
Slog.d(TAG, "no-history finish of " + last + " on new resume");
}
requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
- "no-history");
+ "no-history");
}
}
@@ -2764,7 +2776,8 @@
// If the top activity in the task is the root
// activity, deliver this new intent to it if it
// desires.
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
&& taskTop.realActivity.equals(r.realActivity)) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
if (taskTop.frontOfTask) {
@@ -3064,7 +3077,7 @@
IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, new Intent[] { intent },
+ realCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
@@ -3414,6 +3427,7 @@
ArrayList<ActivityRecord> stops = null;
ArrayList<ActivityRecord> finishes = null;
ArrayList<ActivityRecord> thumbnails = null;
+ ArrayList<UserStartedState> startingUsers = null;
int NS = 0;
int NF = 0;
int NT = 0;
@@ -3495,6 +3509,10 @@
booting = mService.mBooting;
mService.mBooting = false;
}
+ if (mStartingUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+ mStartingUsers.clear();
+ }
}
int i;
@@ -3539,6 +3557,10 @@
if (booting) {
mService.finishBooting();
+ } else if (startingUsers != null) {
+ for (i=0; i<startingUsers.size(); i++) {
+ mService.finishUserSwitch(startingUsers.get(i));
+ }
}
mService.trimApplications();
@@ -3556,6 +3578,10 @@
return res;
}
+ final void addStartingUserLocked(UserStartedState uss) {
+ mStartingUsers.add(uss);
+ }
+
/**
* @return Returns true if the activity is being finished, false if for
* some reason it is being left as-is.
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index 4e6d0fa..07440b5 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -28,14 +28,16 @@
final String packageName;
final String requiredPermission;
final int owningUid;
+ final int owningUserId;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission, int _owningUid) {
+ String _packageName, String _requiredPermission, int _owningUid, int _userId) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
owningUid = _owningUid;
+ owningUserId = _userId;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 7873dd8..1b6ff7a 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -222,7 +222,7 @@
mService.ensurePackageDexOpt(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.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -357,32 +357,23 @@
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky);
+ data, extras, ordered, sticky, sendingUser);
} else {
- receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
}
}
private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
- if (r.onlySendToCaller) {
- if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " not allowed to go to different app " + filter.owningUid);
- skip = true;
- }
- }
- if (!skip && filter.requiredPermission != null) {
+ if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
@@ -438,8 +429,8 @@
+ " (seq=" + seq + "): " + r);
}
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
@@ -589,7 +580,7 @@
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false);
+ r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isnt kept in the mBroadcastHistory.
r.resultTo = null;
@@ -667,18 +658,6 @@
info.activityInfo.name);
boolean skip = false;
- if (r.onlySendToCaller) {
- if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " to " + component.flattenToShortString()
- + " not allowed to go to different app "
- + info.activityInfo.applicationInfo.uid);
- skip = true;
- }
- }
int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 799b609..ca6d5f7 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -44,7 +44,7 @@
final boolean ordered; // serialize the send to receivers?
final boolean sticky; // originated from existing sticky data?
final boolean initialSticky; // initial broadcast from register to sticky?
- final boolean onlySendToCaller; // only allow receipt by sender's components?
+ final int userId; // user id this broadcast was for
final String requiredPermission; // a permission the caller has required
final List receivers; // contains BroadcastFilter and ResolveInfo
IIntentReceiver resultTo; // who receives final result if non-null
@@ -80,7 +80,7 @@
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.println(this);
+ pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
pw.print(prefix); pw.println(intent);
if (sticky) {
Bundle bundle = intent.getExtras();
@@ -141,14 +141,15 @@
pw.println(curReceiver.applicationInfo.sourceDir);
}
}
- String stateStr = " (?)";
- switch (state) {
- case IDLE: stateStr=" (IDLE)"; break;
- case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break;
- case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
- case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
+ if (state != IDLE) {
+ String stateStr = " (?)";
+ switch (state) {
+ case APP_RECEIVE: stateStr=" (APP_RECEIVE)"; break;
+ case CALL_IN_RECEIVE: stateStr=" (CALL_IN_RECEIVE)"; break;
+ case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
+ }
+ pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
}
- pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
final int N = receivers != null ? receivers.size() : 0;
String p2 = prefix + " ";
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
@@ -168,7 +169,8 @@
int _callingPid, int _callingUid, String _requiredPermission,
List _receivers, IIntentReceiver _resultTo, int _resultCode,
String _resultData, Bundle _resultExtras, boolean _serialized,
- boolean _sticky, boolean _initialSticky, boolean _onlySendToCaller) {
+ boolean _sticky, boolean _initialSticky,
+ int _userId) {
queue = _queue;
intent = _intent;
callerApp = _callerApp;
@@ -184,7 +186,7 @@
ordered = _serialized;
sticky = _sticky;
initialSticky = _initialSticky;
- onlySendToCaller = _onlySendToCaller;
+ userId = _userId;
nextReceiver = 0;
state = IDLE;
}
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index d3b8510..0f72409 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -54,11 +54,12 @@
String[] allResolvedTypes;
final int flags;
final int hashCode;
+ final int userId;
private static final int ODD_PRIME_NUMBER = 37;
Key(int _t, String _p, ActivityRecord _a, String _w,
- int _r, Intent[] _i, String[] _it, int _f, Bundle _o) {
+ int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) {
type = _t;
packageName = _p;
activity = _a;
@@ -70,10 +71,12 @@
allResolvedTypes = _it;
flags = _f;
options = _o;
-
+ userId = _userId;
+
int hash = 23;
hash = (ODD_PRIME_NUMBER*hash) + _f;
hash = (ODD_PRIME_NUMBER*hash) + _r;
+ hash = (ODD_PRIME_NUMBER*hash) + _userId;
if (_w != null) {
hash = (ODD_PRIME_NUMBER*hash) + _w.hashCode();
}
@@ -102,6 +105,9 @@
if (type != other.type) {
return false;
}
+ if (userId != other.userId){
+ return false;
+ }
if (!packageName.equals(other.packageName)) {
return false;
}
@@ -156,7 +162,7 @@
+ " intent="
+ (requestIntent != null
? requestIntent.toShortString(false, true, false, false) : "<null>")
- + " flags=0x" + Integer.toHexString(flags) + "}";
+ + " flags=0x" + Integer.toHexString(flags) + " u=" + userId + "}";
}
String typeName() {
@@ -237,11 +243,10 @@
allIntents[allIntents.length-1] = finalIntent;
allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
owner.startActivitiesInPackage(uid, allIntents,
- allResolvedTypes, resultTo, options);
+ allResolvedTypes, resultTo, options, key.userId);
} else {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, 0, options);
+ owner.startActivityInPackage(uid, finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, 0, options, key.userId);
}
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -259,8 +264,7 @@
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false, UserHandle
- .getUserId(uid));
+ requiredPermission, (finishedReceiver != null), false, key.userId);
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -270,7 +274,7 @@
case ActivityManager.INTENT_SENDER_SERVICE:
try {
owner.startServiceInPackage(uid,
- finalIntent, resolvedType);
+ finalIntent, resolvedType, key.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startService intent", e);
@@ -281,7 +285,7 @@
if (sendFinish) {
try {
finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false);
+ null, null, false, false, key.userId);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 15fbb98..7a4fef6 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -116,48 +116,46 @@
}
}
- void removeProviderByName(String name, int optionalUserId) {
+ void removeProviderByName(String name, int userId) {
if (mSingletonByName.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByName name=" + name);
mSingletonByName.remove(name);
} else {
- // TODO: Verify this works, i.e., the caller happens to be from the correct user
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByName name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<String, ContentProviderRecord> map = getProvidersByName(optionalUserId);
+ "Removing from providersByName name=" + name + " user=" + userId);
+ HashMap<String, ContentProviderRecord> map = getProvidersByName(userId);
// map returned by getProvidersByName wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByNamePerUser.remove(optionalUserId);
+ mProvidersByNamePerUser.remove(userId);
}
}
}
- void removeProviderByClass(ComponentName name, int optionalUserId) {
+ void removeProviderByClass(ComponentName name, int userId) {
if (mSingletonByClass.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByClass name=" + name);
mSingletonByClass.remove(name);
} else {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByClass name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(optionalUserId);
+ "Removing from providersByClass name=" + name + " user=" + userId);
+ HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(userId);
// map returned by getProvidersByClass wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByClassPerUser.remove(optionalUserId);
+ mProvidersByClassPerUser.remove(userId);
}
}
}
- private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
if (map == null) {
HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
@@ -168,12 +166,13 @@
}
}
- HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
- final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
+ final HashMap<ComponentName, ContentProviderRecord> map
+ = mProvidersByClassPerUser.get(userId);
if (map == null) {
- HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ HashMap<ComponentName, ContentProviderRecord> newMap
+ = new HashMap<ComponentName, ContentProviderRecord>();
mProvidersByClassPerUser.put(userId, newMap);
return newMap;
} else {
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 32c24c6..9b6701e 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -39,18 +39,20 @@
public final ProcessRecord app;
public final int pid;
public final int uid;
+ public final int userId;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
- int _pid, int _uid, IIntentReceiver _receiver) {
+ int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
owner = _owner;
receiver = _receiver;
app = _app;
pid = _pid;
uid = _uid;
+ userId = _userId;
}
// Want object identity, not the array identity we are inheriting.
@@ -67,8 +69,9 @@
}
void dumpLocal(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("app="); pw.print(app);
- pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.println(uid);
+ pw.print(prefix); pw.print("app="); pw.print(app.toShortString());
+ pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid);
+ pw.print(" user="); pw.println(userId);
if (curBroadcast != null || linkedToDeath) {
pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast);
pw.print(" linkedToDeath="); pw.println(linkedToDeath);
@@ -103,6 +106,8 @@
sb.append((app != null ? app.processName : "(unknown name)"));
sb.append('/');
sb.append(uid);
+ sb.append("/u");
+ sb.append(userId);
sb.append((receiver.asBinder() instanceof Binder) ? " local:" : " remote:");
sb.append(Integer.toHexString(System.identityHashCode(receiver.asBinder())));
sb.append('}');
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 5d60b9c..7055fdc 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -370,7 +370,7 @@
try {
int[] outId = new int[1];
nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
- null, localForegroundId, localForegroundNoti, outId);
+ null, localForegroundId, localForegroundNoti, outId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for service", e);
@@ -399,7 +399,8 @@
return;
}
try {
- inm.cancelNotification(localPackageName, localForegroundId);
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
new file mode 100644
index 0000000..3f3ed85
--- /dev/null
+++ b/services/java/com/android/server/am/UserStartedState.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.am;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.IStopUserCallback;
+import android.os.UserHandle;
+
+public class UserStartedState {
+ public final static int STATE_BOOTING = 0;
+ public final static int STATE_RUNNING = 1;
+ public final static int STATE_STOPPING = 2;
+
+ public final UserHandle mHandle;
+ public final ArrayList<IStopUserCallback> mStopCallbacks
+ = new ArrayList<IStopUserCallback>();
+
+ public int mState = STATE_BOOTING;
+
+ public UserStartedState(UserHandle handle, boolean initial) {
+ mHandle = handle;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mState="); pw.println(mState);
+ }
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e4f1f7a..79fb458 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -43,6 +43,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -417,7 +418,7 @@
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
erroredList);
- mContext.sendStickyBroadcast(broadcast);
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
if (DBG) {
Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
activeList.size() + ", " + erroredList.size());
diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java
index f9fa7a8..abc1d32 100644
--- a/services/java/com/android/server/display/DisplayAdapter.java
+++ b/services/java/com/android/server/display/DisplayAdapter.java
@@ -16,33 +16,113 @@
package com.android.server.display;
+import android.content.Context;
+import android.os.Handler;
+
+import java.io.PrintWriter;
+
/**
* A display adapter makes zero or more display devices available to the system
* and provides facilities for discovering when displays are connected or disconnected.
* <p>
* For now, all display adapters are registered in the system server but
* in principle it could be done from other processes.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public abstract class DisplayAdapter {
+abstract class DisplayAdapter {
+ private final DisplayManagerService.SyncRoot mSyncRoot;
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Listener mListener;
+ private final String mName;
+
+ public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
+ public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
+ public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
+
+ public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, String name) {
+ mSyncRoot = syncRoot;
+ mContext = context;
+ mHandler = handler;
+ mListener = listener;
+ mName = name;
+ }
+
+ /**
+ * Gets the object that the display adapter should synchronize on when handling
+ * calls that come in from outside of the display manager service.
+ */
+ public final DisplayManagerService.SyncRoot getSyncRoot() {
+ return mSyncRoot;
+ }
+
+ /**
+ * Gets the display adapter's context.
+ */
+ public final Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Gets a handler that the display adapter may use to post asynchronous messages.
+ */
+ public final Handler getHandler() {
+ return mHandler;
+ }
+
/**
* Gets the display adapter name for debugging purposes.
- *
- * @return The display adapter name.
*/
- public abstract String getName();
+ public final String getName() {
+ return mName;
+ }
/**
* Registers the display adapter with the display manager.
- * The display adapter should register any built-in display devices now.
- * Other display devices can be registered dynamically later.
*
- * @param listener The listener for callbacks.
+ * The display adapter should register any built-in display devices as soon as possible.
+ * The boot process will wait for the default display to be registered.
+ * Other display devices can be registered dynamically later.
*/
- public abstract void register(Listener listener);
+ public void registerLocked() {
+ }
+
+ /**
+ * Dumps the local state of the display adapter.
+ */
+ public void dumpLocked(PrintWriter pw) {
+ }
+
+ /**
+ * Sends a display device event to the display adapter listener asynchronously.
+ */
+ protected final void sendDisplayDeviceEventLocked(
+ final DisplayDevice device, final int event) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDeviceEvent(device, event);
+ }
+ });
+ }
+
+ /**
+ * Sends a request to perform traversals.
+ */
+ protected final void sendTraversalRequestLocked() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onTraversalRequested();
+ }
+ });
+ }
public interface Listener {
- public void onDisplayDeviceAdded(DisplayDevice device);
- public void onDisplayDeviceRemoved(DisplayDevice device);
+ public void onDisplayDeviceEvent(DisplayDevice device, int event);
+ public void onTraversalRequested();
}
}
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index 57002ff..bdc87f9 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -16,22 +16,150 @@
package com.android.server.display;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.IBinder;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+
/**
* Represents a physical display device such as the built-in display
* an external monitor, or a WiFi display.
+ * <p>
+ * Display devices are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
*/
-public abstract class DisplayDevice {
+abstract class DisplayDevice {
+ private final DisplayAdapter mDisplayAdapter;
+ private final IBinder mDisplayToken;
+
+ // The display device does not manage these properties itself, they are set by
+ // the display manager service. The display device shouldn't really be looking at these.
+ private int mCurrentLayerStack = -1;
+ private int mCurrentOrientation = -1;
+ private Rect mCurrentLayerStackRect;
+ private Rect mCurrentDisplayRect;
+
+ // The display device does own its surface texture, but it should only set it
+ // within a transaction from performTraversalInTransactionLocked.
+ private SurfaceTexture mCurrentSurfaceTexture;
+
+ public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
+ mDisplayAdapter = displayAdapter;
+ mDisplayToken = displayToken;
+ }
+
/**
- * Gets the display adapter that makes the display device available.
+ * Gets the display adapter that owns the display device.
*
* @return The display adapter.
*/
- public abstract DisplayAdapter getAdapter();
+ public final DisplayAdapter getAdapterLocked() {
+ return mDisplayAdapter;
+ }
+
+ /**
+ * Gets the Surface Flinger display token for this display.
+ *
+ * @return The display token, or null if the display is not being managed
+ * by Surface Flinger.
+ */
+ public final IBinder getDisplayTokenLocked() {
+ return mDisplayToken;
+ }
+
+ /**
+ * Gets the name of the display device.
+ *
+ * @return The display device name.
+ */
+ public final String getNameLocked() {
+ return getDisplayDeviceInfoLocked().name;
+ }
/**
* Gets information about the display device.
*
- * @param outInfo The object to populate with the information.
+ * The information returned should not change between calls unless the display
+ * adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event and
+ * {@link #applyPendingDisplayDeviceInfoChangesLocked()} has been called to apply
+ * the pending changes.
+ *
+ * @return The display device info, which should be treated as immutable by the caller.
+ * The display device should allocate a new display device info object whenever
+ * the data changes.
*/
- public abstract void getInfo(DisplayDeviceInfo outInfo);
+ public abstract DisplayDeviceInfo getDisplayDeviceInfoLocked();
+
+ /**
+ * Applies any pending changes to the observable state of the display device
+ * if the display adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event.
+ */
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ }
+
+ /**
+ * Gives the display device a chance to update its properties while in a transaction.
+ */
+ public void performTraversalInTransactionLocked() {
+ }
+
+ /**
+ * Sets the display layer stack while in a transaction.
+ */
+ public final void setLayerStackInTransactionLocked(int layerStack) {
+ if (mCurrentLayerStack == layerStack) {
+ return;
+ }
+ mCurrentLayerStack = layerStack;
+ Surface.setDisplayLayerStack(mDisplayToken, layerStack);
+ }
+
+ /**
+ * Sets the display projection while in a transaction.
+ *
+ * @param orientation defines the display's orientation
+ * @param layerStackRect defines which area of the window manager coordinate
+ * space will be used
+ * @param displayRect defines where on the display will layerStackRect be
+ * mapped to. displayRect is specified post-orientation, that is
+ * it uses the orientation seen by the end-user
+ */
+ public final void setProjectionInTransactionLocked(int orientation, Rect layerStackRect, Rect displayRect) {
+ mCurrentOrientation = orientation;
+ if (mCurrentLayerStackRect == null) {
+ mCurrentLayerStackRect = new Rect();
+ }
+ mCurrentLayerStackRect.set(layerStackRect);
+ if (mCurrentDisplayRect == null) {
+ mCurrentDisplayRect = new Rect();
+ }
+ mCurrentDisplayRect.set(displayRect);
+ Surface.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
+ }
+
+ /**
+ * Sets the surface texture while in a transaction.
+ */
+ public final void setSurfaceTextureInTransactionLocked(SurfaceTexture surfaceTexture) {
+ if (mCurrentSurfaceTexture == surfaceTexture) {
+ return;
+ }
+ mCurrentSurfaceTexture = surfaceTexture;
+ Surface.setDisplaySurface(mDisplayToken, surfaceTexture);
+ }
+
+ /**
+ * Dumps the local state of the display device.
+ * Does not need to dump the display device info because that is already dumped elsewhere.
+ */
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mAdapter=" + mDisplayAdapter.getName());
+ pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentOrientation=" + mCurrentOrientation);
+ pw.println("mCurrentViewport=" + mCurrentLayerStackRect);
+ pw.println("mCurrentFrame=" + mCurrentDisplayRect);
+ pw.println("mCurrentSurfaceTexture=" + mCurrentSurfaceTexture);
+ }
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index 9c0f964..6f82119 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,10 +16,30 @@
package com.android.server.display;
+import libcore.util.Objects;
+
/**
* Describes the characteristics of a physical display device.
*/
-public final class DisplayDeviceInfo {
+final class DisplayDeviceInfo {
+ /**
+ * Flag: Indicates that this display device should be considered the default display
+ * device of the system.
+ */
+ public static final int FLAG_DEFAULT_DISPLAY = 1 << 0;
+
+ /**
+ * Flag: Indicates that this display device can show secure surfaces.
+ */
+ public static final int FLAG_SECURE = 1 << 1;
+
+ /**
+ * Flag: Indicates that this display device can rotate to show contents in a
+ * different orientation. Otherwise the rotation is assumed to be fixed in the
+ * natural orientation and the display manager should transform the content to fit.
+ */
+ public static final int FLAG_SUPPORTS_ROTATION = 1 << 2;
+
/**
* Gets the name of the display device, which may be derived from
* EDID or other sources. The name may be displayed to the user.
@@ -43,6 +63,30 @@
public float xDpi;
public float yDpi;
+ public int flags;
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayDeviceInfo && equals((DisplayDeviceInfo)o);
+ }
+
+ public boolean equals(DisplayDeviceInfo other) {
+ return other != null
+ && Objects.equal(name, other.name)
+ && width == other.width
+ && height == other.height
+ && refreshRate == other.refreshRate
+ && densityDpi == other.densityDpi
+ && xDpi == other.xDpi
+ && yDpi == other.yDpi
+ && flags == other.flags;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
public void copyFrom(DisplayDeviceInfo other) {
name = other.name;
width = other.width;
@@ -51,12 +95,25 @@
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
+ flags = other.flags;
}
// For debugging purposes
@Override
public String toString() {
- return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
- + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi";
+ return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+ + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
+ + flagsToString(flags) + "}";
+ }
+
+ private static String flagsToString(int flags) {
+ StringBuilder msg = new StringBuilder();
+ if ((flags & FLAG_DEFAULT_DISPLAY) != 0) {
+ msg.append(", FLAG_DEFAULT_DISPLAY");
+ }
+ if ((flags & FLAG_SECURE) != 0) {
+ msg.append(", FLAG_SECURE");
+ }
+ return msg.toString();
}
}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 2ebad1d..706007a 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -16,63 +16,189 @@
package com.android.server.display;
+import com.android.internal.util.IndentingPrintWriter;
+
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IDisplayManager;
+import android.hardware.display.IDisplayManagerCallback;
import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.Surface;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
- * Manages the properties, media routing and power state of attached displays.
+ * Manages attached displays.
* <p>
- * The display manager service does not own or directly control the displays.
- * Instead, other components in the system register their display adapters with the
- * display manager service which acts as a central controller.
+ * The {@link DisplayManagerService} manages the global lifecycle of displays,
+ * decides how to configure logical displays based on the physical display devices currently
+ * attached, sends notifications to the system and to applications when the state
+ * changes, and so on.
+ * </p><p>
+ * The display manager service relies on a collection of {@link DisplayAdapter} components,
+ * for discovering and configuring physical display devices attached to the system.
+ * There are separate display adapters for each manner that devices are attached:
+ * one display adapter for built-in local displays, one for simulated non-functional
+ * displays when the system is headless, one for simulated overlay displays used for
+ * development, one for wifi displays, etc.
+ * </p><p>
+ * Display adapters are only weakly coupled to the display manager service.
+ * Display adapters communicate changes in display device state to the display manager
+ * service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered
+ * by the display manager service. This separation of concerns is important for
+ * two main reasons. First, it neatly encapsulates the responsibilities of these
+ * two classes: display adapters handle individual display devices whereas
+ * the display manager service handles the global state. Second, it eliminates
+ * the potential for deadlocks resulting from asynchronous display device discovery.
+ * </p>
+ *
+ * <h3>Synchronization</h3>
+ * <p>
+ * Because the display manager may be accessed by multiple threads, the synchronization
+ * story gets a little complicated. In particular, the window manager may call into
+ * the display manager while holding a surface transaction with the expectation that
+ * it can apply changes immediately. Unfortunately, that means we can't just do
+ * everything asynchronously (*grump*).
+ * </p><p>
+ * To make this work, all of the objects that belong to the display manager must
+ * use the same lock. We call this lock the synchronization root and it has a unique
+ * type {@link DisplayManagerService.SyncRoot}. Methods that require this lock are
+ * named with the "Locked" suffix.
+ * </p><p>
+ * Where things get tricky is that the display manager is not allowed to make
+ * any potentially reentrant calls, especially into the window manager. We generally
+ * avoid this by making all potentially reentrant out-calls asynchronous.
* </p>
*/
public final class DisplayManagerService extends IDisplayManager.Stub {
private static final String TAG = "DisplayManagerService";
+ private static final boolean DEBUG = false;
private static final String SYSTEM_HEADLESS = "ro.config.headless";
+ private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
- private final Object mLock = new Object();
+ private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1;
+ private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
+ private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
+ private static final int MSG_REQUEST_TRAVERSAL = 4;
private final Context mContext;
private final boolean mHeadless;
+ private final DisplayManagerHandler mHandler;
+ private final Handler mUiHandler;
+ private final DisplayAdapterListener mDisplayAdapterListener;
+ private WindowManagerFuncs mWindowManagerFuncs;
+ // The synchronization root for the display manager.
+ // This lock guards most of the display manager's state.
+ private final SyncRoot mSyncRoot = new SyncRoot();
+
+ // True if in safe mode.
+ // This option may disable certain display adapters.
+ public boolean mSafeMode;
+
+ // True if we are in a special boot mode where only core applications and
+ // services should be started. This option may disable certain display adapters.
+ public boolean mOnlyCore;
+
+ // All callback records indexed by calling process id.
+ public final SparseArray<CallbackRecord> mCallbacks =
+ new SparseArray<CallbackRecord>();
+
+ // List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
- private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
- public DisplayManagerService(Context context) {
+ // List of all currently connected display devices.
+ private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
+
+ // List of all removed display devices.
+ private final ArrayList<DisplayDevice> mRemovedDisplayDevices = new ArrayList<DisplayDevice>();
+
+ // List of all logical displays indexed by logical display id.
+ private final SparseArray<LogicalDisplay> mLogicalDisplays =
+ new SparseArray<LogicalDisplay>();
+ private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
+
+ // Set to true when there are pending display changes that have yet to be applied
+ // to the surface flinger state.
+ private boolean mPendingTraversal;
+
+ // Temporary callback list, used when sending display events to applications.
+ // May be used outside of the lock but only on the handler thread.
+ private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
+
+ // Temporary display info, used for comparing display configurations.
+ private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+
+ public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
mContext = context;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
- registerDefaultDisplayAdapter();
+ mHandler = new DisplayManagerHandler(mainHandler.getLooper());
+ mUiHandler = uiHandler;
+ mDisplayAdapterListener = new DisplayAdapterListener();
+
+ mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
}
- private void registerDefaultDisplayAdapter() {
- if (mHeadless) {
- registerDisplayAdapter(new HeadlessDisplayAdapter(mContext));
- } else {
- registerDisplayAdapter(new SurfaceFlingerDisplayAdapter(mContext));
+ /**
+ * Pauses the boot process to wait for the first display to be initialized.
+ */
+ public boolean waitForDefaultDisplay() {
+ synchronized (mSyncRoot) {
+ long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
+ while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
+ long delay = timeout - SystemClock.uptimeMillis();
+ if (delay <= 0) {
+ return false;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
+ }
+ try {
+ mSyncRoot.wait(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called during initialization to associated the display manager with the
+ * window manager.
+ */
+ public void setWindowManager(WindowManagerFuncs windowManagerFuncs) {
+ synchronized (mSyncRoot) {
+ mWindowManagerFuncs = windowManagerFuncs;
+ scheduleTraversalLocked();
}
}
- // FIXME: this isn't the right API for the long term
- public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) {
- // hardcoded assuming 720p touch screen plugged into HDMI and USB
- // need to redesign this
- info.width = 1280;
- info.height = 720;
+ /**
+ * Called when the system is ready to go.
+ */
+ public void systemReady(boolean safeMode, boolean onlyCore) {
+ synchronized (mSyncRoot) {
+ mSafeMode = safeMode;
+ mOnlyCore = onlyCore;
+ }
+
+ mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
}
/**
@@ -85,71 +211,349 @@
}
/**
- * Save away new DisplayInfo data.
- * @param displayId The local DisplayInfo to store the new data in.
+ * Overrides the display information of a particular logical display.
+ * This is used by the window manager to control the size and characteristics
+ * of the default display. It is expected to apply the requested change
+ * to the display information synchronously so that applications will immediately
+ * observe the new state.
+ *
+ * @param displayId The logical display id.
* @param info The new data to be stored.
*/
- public void setDisplayInfo(int displayId, DisplayInfo info) {
- synchronized (mLock) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- throw new UnsupportedOperationException();
+ public void setDisplayInfoOverrideFromWindowManager(
+ int displayId, DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.setDisplayInfoOverrideFromWindowManagerLocked(info);
+ if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ scheduleTraversalLocked();
+ }
}
- mDefaultDisplayInfo.copyFrom(info);
}
}
/**
- * Return requested DisplayInfo.
- * @param displayId The data to retrieve.
- * @param outInfo The structure to receive the data.
+ * Called by the window manager to perform traversals while holding a
+ * surface flinger transaction.
*/
- @Override // Binder call
- public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) {
- synchronized (mLock) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- return false;
+ public void performTraversalInTransactionFromWindowManager() {
+ synchronized (mSyncRoot) {
+ if (!mPendingTraversal) {
+ return;
}
- outInfo.copyFrom(mDefaultDisplayInfo);
- return true;
+ mPendingTraversal = false;
+
+ performTraversalInTransactionLocked();
}
}
- private void registerDisplayAdapter(DisplayAdapter adapter) {
- mDisplayAdapters.add(adapter);
- adapter.register(new DisplayAdapter.Listener() {
- @Override
- public void onDisplayDeviceAdded(DisplayDevice device) {
- DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
- device.getInfo(deviceInfo);
- copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo);
+ /**
+ * Returns information about the specified logical display.
+ *
+ * @param displayId The logical display id.
+ * @param The logical display info, or null if the display does not exist.
+ * This object must be treated as immutable.
+ */
+ @Override // Binder call
+ public DisplayInfo getDisplayInfo(int displayId) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ return display.getDisplayInfoLocked();
}
-
- @Override
- public void onDisplayDeviceRemoved(DisplayDevice device) {
- }
- });
+ return null;
+ }
}
- private void copyDisplayInfoFromDeviceInfo(
- DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) {
- // Bootstrap the logical display using the physical display.
- displayInfo.appWidth = deviceInfo.width;
- displayInfo.appHeight = deviceInfo.height;
- displayInfo.logicalWidth = deviceInfo.width;
- displayInfo.logicalHeight = deviceInfo.height;
- displayInfo.rotation = Surface.ROTATION_0;
- displayInfo.refreshRate = deviceInfo.refreshRate;
- displayInfo.logicalDensityDpi = deviceInfo.densityDpi;
- displayInfo.physicalXDpi = deviceInfo.xDpi;
- displayInfo.physicalYDpi = deviceInfo.yDpi;
- displayInfo.smallestNominalAppWidth = deviceInfo.width;
- displayInfo.smallestNominalAppHeight = deviceInfo.height;
- displayInfo.largestNominalAppWidth = deviceInfo.width;
- displayInfo.largestNominalAppHeight = deviceInfo.height;
+ /**
+ * Returns the list of all display ids.
+ */
+ @Override // Binder call
+ public int[] getDisplayIds() {
+ synchronized (mSyncRoot) {
+ final int count = mLogicalDisplays.size();
+ int[] displayIds = new int[count];
+ for (int i = 0; i < count; i++) {
+ displayIds[i] = mLogicalDisplays.keyAt(i);
+ }
+ return displayIds;
+ }
}
@Override // Binder call
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void registerCallback(IDisplayManagerCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ synchronized (mSyncRoot) {
+ int callingPid = Binder.getCallingPid();
+ if (mCallbacks.get(callingPid) != null) {
+ throw new SecurityException("The calling process has already "
+ + "registered an IDisplayManagerCallback.");
+ }
+
+ CallbackRecord record = new CallbackRecord(callingPid, callback);
+ try {
+ IBinder binder = callback.asBinder();
+ binder.linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ // give up
+ throw new RuntimeException(ex);
+ }
+
+ mCallbacks.put(callingPid, record);
+ }
+ }
+
+ private void onCallbackDied(int pid) {
+ synchronized (mSyncRoot) {
+ mCallbacks.remove(pid);
+ }
+ }
+
+ private void registerDefaultDisplayAdapter() {
+ // Register default display adapter.
+ synchronized (mSyncRoot) {
+ if (mHeadless) {
+ registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ } else {
+ registerDisplayAdapterLocked(new LocalDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ }
+ }
+ }
+
+ private void registerAdditionalDisplayAdapters() {
+ synchronized (mSyncRoot) {
+ if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
+ registerDisplayAdapterLocked(new OverlayDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+ }
+ }
+ }
+
+ private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
+ // In safe mode, we disable non-essential display adapters to give the user
+ // an opportunity to fix broken settings or other problems that might affect
+ // system stability.
+ // In only-core mode, we disable non-essential display adapters to minimize
+ // the number of dependencies that are started while in this mode and to
+ // prevent problems that might occur due to the device being encrypted.
+ return !mSafeMode && !mOnlyCore;
+ }
+
+ private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
+ mDisplayAdapters.add(adapter);
+ adapter.registerLocked();
+ }
+
+ private void handleDisplayDeviceAdded(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ mDisplayDevices.add(device);
+ addLogicalDisplayLocked(device);
+ scheduleTraversalLocked();
+ }
+ }
+
+ private void handleDisplayDeviceChanged(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (!mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to change non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ device.applyPendingDisplayDeviceInfoChangesLocked();
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked();
+ }
+ }
+ }
+
+ private void handleDisplayDeviceRemoved(DisplayDevice device) {
+ synchronized (mSyncRoot) {
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
+
+ mRemovedDisplayDevices.add(device);
+ updateLogicalDisplaysLocked();
+ scheduleTraversalLocked();
+ }
+ }
+
+ // Adds a new logical display based on the given display device.
+ // Sends notifications if needed.
+ private void addLogicalDisplayLocked(DisplayDevice device) {
+ DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
+ boolean isDefault = (deviceInfo.flags
+ & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
+ if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
+ Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
+ isDefault = false;
+ }
+
+ final int displayId = assignDisplayIdLocked(isDefault);
+ final int layerStack = assignLayerStackLocked(displayId);
+
+ LogicalDisplay display = new LogicalDisplay(layerStack, device);
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ // This should never happen currently.
+ Slog.w(TAG, "Ignoring display device because the logical display "
+ + "created from it was not considered valid: " + deviceInfo);
+ return;
+ }
+
+ mLogicalDisplays.put(displayId, display);
+
+ // Wake up waitForDefaultDisplay.
+ if (isDefault) {
+ mSyncRoot.notifyAll();
+ }
+
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ }
+
+ private int assignDisplayIdLocked(boolean isDefault) {
+ return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
+ }
+
+ private int assignLayerStackLocked(int displayId) {
+ // Currently layer stacks and display ids are the same.
+ // This need not be the case.
+ return displayId;
+ }
+
+ // Updates all existing logical displays given the current set of display devices.
+ // Removes invalid logical displays.
+ // Sends notifications if needed.
+ private boolean updateLogicalDisplaysLocked() {
+ boolean changed = false;
+ for (int i = mLogicalDisplays.size(); i-- > 0; ) {
+ final int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ mLogicalDisplays.removeAt(i);
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ changed = true;
+ } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ private void performTraversalInTransactionLocked() {
+ // Perform one last traversal for each removed display device.
+ final int removedCount = mRemovedDisplayDevices.size();
+ for (int i = 0; i < removedCount; i++) {
+ DisplayDevice device = mRemovedDisplayDevices.get(i);
+ device.performTraversalInTransactionLocked();
+ }
+ mRemovedDisplayDevices.clear();
+
+ // Configure each display device.
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ DisplayDevice device = mDisplayDevices.get(i);
+ configureDisplayInTransactionLocked(device);
+ device.performTraversalInTransactionLocked();
+ }
+ }
+
+ private void configureDisplayInTransactionLocked(DisplayDevice device) {
+ // TODO: add a proper per-display mirroring control
+ boolean isMirroring = SystemProperties.getBoolean("debug.display.mirror", true);
+
+ // Find the logical display that the display device is showing.
+ LogicalDisplay display = null;
+ if (!isMirroring) {
+ display = findLogicalDisplayForDeviceLocked(device);
+ }
+ if (display == null) {
+ display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ }
+
+ // Apply the logical display configuration to the display device.
+ if (display == null) {
+ // TODO: no logical display for the device, blank it
+ Slog.d(TAG, "Missing logical display to use for physical display device: "
+ + device.getDisplayDeviceInfoLocked());
+ } else {
+ display.configureDisplayInTransactionLocked(device);
+ }
+ }
+
+ private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
+ final int count = mLogicalDisplays.size();
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ if (display.getPrimaryDisplayDeviceLocked() == device) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ private void sendDisplayEventLocked(int displayId, int event) {
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+ mHandler.sendMessage(msg);
+ }
+
+ // Requests that performTraversalsInTransactionFromWindowManager be called at a
+ // later time to apply changes to surfaces and displays.
+ private void scheduleTraversalLocked() {
+ if (!mPendingTraversal && mWindowManagerFuncs != null) {
+ mPendingTraversal = true;
+ mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL);
+ }
+ }
+
+ // Runs on Handler thread.
+ // Delivers display event notifications to callbacks.
+ private void deliverDisplayEvent(int displayId, int event) {
+ if (DEBUG) {
+ Slog.d(TAG, "Delivering display event: displayId="
+ + displayId + ", event=" + event);
+ }
+
+ // Grab the lock and copy the callbacks.
+ final int count;
+ synchronized (mSyncRoot) {
+ count = mCallbacks.size();
+ mTempCallbacks.clear();
+ for (int i = 0; i < count; i++) {
+ mTempCallbacks.add(mCallbacks.valueAt(i));
+ }
+ }
+
+ // After releasing the lock, send the notifications out.
+ for (int i = 0; i < count; i++) {
+ mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
+ }
+ mTempCallbacks.clear();
+ }
+
+ @Override // Binder call
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (mContext == null
|| mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -158,19 +562,137 @@
return;
}
- pw.println("DISPLAY MANAGER (dumpsys display)\n");
+ pw.println("DISPLAY MANAGER (dumpsys display)");
+ pw.println(" mHeadless=" + mHeadless);
- pw.println("Headless: " + mHeadless);
+ synchronized (mSyncRoot) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
- synchronized (mLock) {
+ pw.println();
+ pw.println("Display Adapters: size=" + mDisplayAdapters.size());
for (DisplayAdapter adapter : mDisplayAdapters) {
- pw.println("Adapter: " + adapter.getName());
+ pw.println(" " + adapter.getName());
+ adapter.dumpLocked(ipw);
}
- pw.println("Default display info: " + mDefaultDisplayInfo);
+ pw.println();
+ pw.println("Display Devices: size=" + mDisplayDevices.size());
+ for (DisplayDevice device : mDisplayDevices) {
+ pw.println(" " + device.getDisplayDeviceInfoLocked());
+ device.dumpLocked(ipw);
+ }
+
+ final int logicalDisplayCount = mLogicalDisplays.size();
+ pw.println();
+ pw.println("Logical Displays: size=" + logicalDisplayCount);
+ for (int i = 0; i < logicalDisplayCount; i++) {
+ int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ pw.println(" Display " + displayId + ":");
+ display.dumpLocked(ipw);
+ }
+ }
+ }
+
+ /**
+ * This is the object that everything in the display manager locks on.
+ * We make it an inner class within the {@link DisplayManagerService} to so that it is
+ * clear that the object belongs to the display manager service and that it is
+ * a unique object with a special purpose.
+ */
+ public static final class SyncRoot {
+ }
+
+ /**
+ * Private interface to the window manager.
+ */
+ public interface WindowManagerFuncs {
+ /**
+ * Request that the window manager call {@link #performTraversalInTransaction}
+ * within a surface transaction at a later time.
+ */
+ void requestTraversal();
+ }
+
+ private final class DisplayManagerHandler extends Handler {
+ public DisplayManagerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
}
- pw.println("Default display: "
- + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY));
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
+ registerDefaultDisplayAdapter();
+ break;
+
+ case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
+ registerAdditionalDisplayAdapters();
+ break;
+
+ case MSG_DELIVER_DISPLAY_EVENT:
+ deliverDisplayEvent(msg.arg1, msg.arg2);
+ break;
+
+ case MSG_REQUEST_TRAVERSAL:
+ mWindowManagerFuncs.requestTraversal();
+ break;
+ }
+ }
+ }
+
+ private final class DisplayAdapterListener implements DisplayAdapter.Listener {
+ @Override
+ public void onDisplayDeviceEvent(DisplayDevice device, int event) {
+ switch (event) {
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
+ handleDisplayDeviceAdded(device);
+ break;
+
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
+ handleDisplayDeviceChanged(device);
+ break;
+
+ case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
+ handleDisplayDeviceRemoved(device);
+ break;
+ }
+ }
+
+ @Override
+ public void onTraversalRequested() {
+ synchronized (mSyncRoot) {
+ scheduleTraversalLocked();
+ }
+ }
+ }
+
+ private final class CallbackRecord implements DeathRecipient {
+ private final int mPid;
+ private final IDisplayManagerCallback mCallback;
+
+ public CallbackRecord(int pid, IDisplayManagerCallback callback) {
+ mPid = pid;
+ mCallback = callback;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Display listener for pid " + mPid + " died.");
+ }
+ onCallbackDied(mPid);
+ }
+
+ public void notifyDisplayEventAsync(int displayId, int event) {
+ try {
+ mCallback.onDisplayEvent(displayId, event);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process "
+ + mPid + " that displays changed, assuming it died.", ex);
+ binderDied();
+ }
+ }
}
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index 17c2360..3aaacf1 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -17,46 +17,52 @@
package com.android.server.display;
import android.content.Context;
+import android.os.Handler;
import android.util.DisplayMetrics;
/**
* Provides a fake default display for headless systems.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
*/
-public final class HeadlessDisplayAdapter extends DisplayAdapter {
- private final Context mContext;
- private final HeadlessDisplayDevice mDefaultDisplayDevice;
+final class HeadlessDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "HeadlessDisplayAdapter";
- public HeadlessDisplayAdapter(Context context) {
- mContext = context;
- mDefaultDisplayDevice = new HeadlessDisplayDevice();
+ public HeadlessDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
}
@Override
- public String getName() {
- return "HeadlessDisplayAdapter";
- }
-
- @Override
- public void register(Listener listener) {
- listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
+ public void registerLocked() {
+ super.registerLocked();
+ sendDisplayDeviceEventLocked(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED);
}
private final class HeadlessDisplayDevice extends DisplayDevice {
- @Override
- public DisplayAdapter getAdapter() {
- return HeadlessDisplayAdapter.this;
+ private DisplayDeviceInfo mInfo;
+
+ public HeadlessDisplayDevice() {
+ super(HeadlessDisplayAdapter.this, null);
}
@Override
- public void getInfo(DisplayDeviceInfo outInfo) {
- outInfo.name = mContext.getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display);
- outInfo.width = 640;
- outInfo.height = 480;
- outInfo.refreshRate = 60;
- outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
- outInfo.xDpi = 160;
- outInfo.yDpi = 160;
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.width = 640;
+ mInfo.height = 480;
+ mInfo.refreshRate = 60;
+ mInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ mInfo.xDpi = 160;
+ mInfo.yDpi = 160;
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE;
+ }
+ return mInfo;
}
}
}
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
new file mode 100644
index 0000000..4a8829a
--- /dev/null
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.SparseArray;
+import android.view.Surface;
+import android.view.Surface.PhysicalDisplayInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * A display adapter for the local displays managed by Surface Flinger.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class LocalDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "LocalDisplayAdapter";
+
+ private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
+ Surface.BUILT_IN_DISPLAY_ID_MAIN,
+ Surface.BUILT_IN_DISPLAY_ID_HDMI,
+ };
+
+ private final SparseArray<LocalDisplayDevice> mDevices =
+ new SparseArray<LocalDisplayDevice>();
+
+ private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo();
+
+ public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ @Override
+ public void registerLocked() {
+ // TODO: listen for notifications from Surface Flinger about
+ // built-in displays being added or removed and rescan as needed.
+ super.registerLocked();
+ scanDisplaysLocked();
+ }
+
+ private void scanDisplaysLocked() {
+ for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
+ IBinder displayToken = Surface.getBuiltInDisplay(builtInDisplayId);
+ if (displayToken != null && Surface.getDisplayInfo(displayToken, mTempPhys)) {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device == null) {
+ // Display was added.
+ device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
+ mDevices.put(builtInDisplayId, device);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+ } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
+ // Display properties changed.
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ } else {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device != null) {
+ // Display was removed.
+ mDevices.remove(builtInDisplayId);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
+ }
+
+ private final class LocalDisplayDevice extends DisplayDevice {
+ private final int mBuiltInDisplayId;
+ private final PhysicalDisplayInfo mPhys;
+
+ private DisplayDeviceInfo mInfo;
+ private boolean mHavePendingChanges;
+
+ public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
+ PhysicalDisplayInfo phys) {
+ super(LocalDisplayAdapter.this, displayToken);
+ mBuiltInDisplayId = builtInDisplayId;
+ mPhys = new PhysicalDisplayInfo(phys);
+ }
+
+ public boolean updatePhysicalDisplayInfoLocked(PhysicalDisplayInfo phys) {
+ if (!mPhys.equals(phys)) {
+ mPhys.copyFrom(phys);
+ mHavePendingChanges = true;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ if (mHavePendingChanges) {
+ mInfo = null;
+ mHavePendingChanges = false;
+ }
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.width = mPhys.width;
+ mInfo.height = mPhys.height;
+ mInfo.refreshRate = mPhys.refreshRate;
+ mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
+ mInfo.xDpi = mPhys.xDpi;
+ mInfo.yDpi = mPhys.yDpi;
+ if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE
+ | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
+ } else {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_hdmi_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ }
+ }
+ return mInfo;
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+ pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+ pw.println("mPhys=" + mPhys);
+ }
+ }
+}
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
new file mode 100644
index 0000000..c864189
--- /dev/null
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.display;
+
+import android.graphics.Rect;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import libcore.util.Objects;
+
+/**
+ * Describes how a logical display is configured.
+ * <p>
+ * At this time, we only support logical displays that are coupled to a particular
+ * primary display device from which the logical display derives its basic properties
+ * such as its size, density and refresh rate.
+ * </p><p>
+ * A logical display may be mirrored onto multiple display devices in addition to its
+ * primary display device. Note that the contents of a logical display may not
+ * always be visible, even on its primary display device, such as in the case where
+ * the primary display device is currently mirroring content from a different
+ * logical display.
+ * </p><p>
+ * This object is designed to encapsulate as much of the policy of logical
+ * displays as possible. The idea is to make it easy to implement new kinds of
+ * logical displays mostly by making local changes to this class.
+ * </p><p>
+ * Note: The display manager architecture does not actually require logical displays
+ * to be associated with any individual display device. Logical displays and
+ * display devices are orthogonal concepts. Some mapping will exist between
+ * logical displays and display devices but it can be many-to-many and
+ * and some might have no relation at all.
+ * </p><p>
+ * Logical displays are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class LogicalDisplay {
+ private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
+
+ private final int mLayerStack;
+ private DisplayInfo mOverrideDisplayInfo; // set by the window manager
+ private DisplayInfo mInfo;
+
+ // The display device that this logical display is based on and which
+ // determines the base metrics that it uses.
+ private DisplayDevice mPrimaryDisplayDevice;
+ private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
+
+ // Temporary rectangle used when needed.
+ private final Rect mTempLayerStackRect = new Rect();
+ private final Rect mTempDisplayRect = new Rect();
+
+ public LogicalDisplay(int layerStack, DisplayDevice primaryDisplayDevice) {
+ mLayerStack = layerStack;
+ mPrimaryDisplayDevice = primaryDisplayDevice;
+ }
+
+ /**
+ * Gets the primary display device associated with this logical display.
+ *
+ * @return The primary display device.
+ */
+ public DisplayDevice getPrimaryDisplayDeviceLocked() {
+ return mPrimaryDisplayDevice;
+ }
+
+ /**
+ * Gets information about the logical display.
+ *
+ * @return The device info, which should be treated as immutable by the caller.
+ * The logical display should allocate a new display info object whenever
+ * the data changes.
+ */
+ public DisplayInfo getDisplayInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayInfo();
+ if (mOverrideDisplayInfo != null) {
+ mInfo.copyFrom(mOverrideDisplayInfo);
+ mInfo.layerStack = mBaseDisplayInfo.layerStack;
+ mInfo.name = mBaseDisplayInfo.name;
+ } else {
+ mInfo.copyFrom(mBaseDisplayInfo);
+ }
+ }
+ return mInfo;
+ }
+
+ /**
+ * Sets overridden logical display information from the window manager.
+ * This method can be used to adjust application insets, rotation, and other
+ * properties that the window manager takes care of.
+ *
+ * @param info The logical display information, may be null.
+ */
+ public void setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
+ if (info != null) {
+ if (mOverrideDisplayInfo == null) {
+ mOverrideDisplayInfo = new DisplayInfo(info);
+ mInfo = null;
+ } else if (!mOverrideDisplayInfo.equals(info)) {
+ mOverrideDisplayInfo.copyFrom(info);
+ mInfo = null;
+ }
+ } else if (mOverrideDisplayInfo != null) {
+ mOverrideDisplayInfo = null;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Returns true if the logical display is in a valid state.
+ * This method should be checked after calling {@link #update} to handle the
+ * case where a logical display should be removed because all of its associated
+ * display devices are gone or if it is otherwise no longer needed.
+ *
+ * @return True if the logical display is still valid.
+ */
+ public boolean isValidLocked() {
+ return mPrimaryDisplayDevice != null;
+ }
+
+ /**
+ * Updates the state of the logical display based on the available display devices.
+ * The logical display might become invalid if it is attached to a display device
+ * that no longer exists.
+ *
+ * @param devices The list of all connected display devices.
+ */
+ public void updateLocked(List<DisplayDevice> devices) {
+ // Nothing to update if already invalid.
+ if (mPrimaryDisplayDevice == null) {
+ return;
+ }
+
+ // Check whether logical display has become invalid.
+ if (!devices.contains(mPrimaryDisplayDevice)) {
+ mPrimaryDisplayDevice = null;
+ return;
+ }
+
+ // Bootstrap the logical display using its associated primary physical display.
+ // We might use more elaborate configurations later. It's possible that the
+ // configuration of several physical displays might be used to determine the
+ // logical display that they are sharing. (eg. Adjust size for pixel-perfect
+ // mirroring over HDMI.)
+ DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked();
+ if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {
+ mBaseDisplayInfo.layerStack = mLayerStack;
+ mBaseDisplayInfo.name = deviceInfo.name;
+ mBaseDisplayInfo.appWidth = deviceInfo.width;
+ mBaseDisplayInfo.appHeight = deviceInfo.height;
+ mBaseDisplayInfo.logicalWidth = deviceInfo.width;
+ mBaseDisplayInfo.logicalHeight = deviceInfo.height;
+ mBaseDisplayInfo.rotation = Surface.ROTATION_0;
+ mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate;
+ mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
+ mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
+ mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+ mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
+ mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+
+ mPrimaryDisplayDeviceInfo = deviceInfo;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Applies the layer stack and transformation to the given display device
+ * so that it shows the contents of this logical display.
+ *
+ * We know that the given display device is only ever showing the contents of
+ * a single logical display, so this method is expected to blow away all of its
+ * transformation properties to make it happen regardless of what the
+ * display device was previously showing.
+ *
+ * The caller must have an open Surface transaction.
+ *
+ * The display device may not be the primary display device, in the case
+ * where the display is being mirrored.
+ *
+ * @param device The display device to modify.
+ */
+ public void configureDisplayInTransactionLocked(DisplayDevice device) {
+ final DisplayInfo displayInfo = getDisplayInfoLocked();
+ final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
+
+ // Set the layer stack.
+ device.setLayerStackInTransactionLocked(mLayerStack);
+
+ // Set the viewport.
+ // This is the area of the logical display that we intend to show on the
+ // display device. For now, it is always the full size of the logical display.
+ mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ // Set the orientation.
+ // The orientation specifies how the physical coordinate system of the display
+ // is rotated when the contents of the logical display are rendered.
+ int orientation = Surface.ROTATION_0;
+ if (device == mPrimaryDisplayDevice
+ && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
+ orientation = displayInfo.rotation;
+ }
+
+ // Set the frame.
+ // The frame specifies the rotated physical coordinates into which the viewport
+ // is mapped. We need to take care to preserve the aspect ratio of the viewport.
+ // Currently we maximize the area to fill the display, but we could try to be
+ // more clever and match resolutions.
+ boolean rotated = (orientation == Surface.ROTATION_90
+ || orientation == Surface.ROTATION_270);
+ int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
+ int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
+
+ // Determine whether the width or height is more constrained to be scaled.
+ // physWidth / displayInfo.logicalWidth => letter box
+ // or physHeight / displayInfo.logicalHeight => pillar box
+ //
+ // We avoid a division (and possible floating point imprecision) here by
+ // multiplying the fractions by the product of their denominators before
+ // comparing them.
+ int displayRectWidth, displayRectHeight;
+ if (physWidth * displayInfo.logicalHeight
+ < physHeight * displayInfo.logicalWidth) {
+ // Letter box.
+ displayRectWidth = physWidth;
+ displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
+ } else {
+ // Pillar box.
+ displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
+ displayRectHeight = physHeight;
+ }
+ int displayRectTop = (physHeight - displayRectHeight) / 2;
+ int displayRectLeft = (physWidth - displayRectWidth) / 2;
+ mTempDisplayRect.set(displayRectLeft, displayRectTop,
+ displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
+
+ device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
+ mPrimaryDisplayDevice.getNameLocked() : "null"));
+ pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
+ pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
new file mode 100644
index 0000000..ea7e88d
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.SurfaceTexture;
+import android.os.Handler;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A display adapter that uses overlay windows to simulate secondary displays
+ * for development purposes. Use Development Settings to enable one or more
+ * overlay displays.
+ * <p>
+ * This object has two different handlers (which may be the same) which must not
+ * get confused. The main handler is used to posting messages to the display manager
+ * service as usual. The UI handler is only used by the {@link OverlayDisplayWindow}.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class OverlayDisplayAdapter extends DisplayAdapter {
+ static final String TAG = "OverlayDisplayAdapter";
+ static final boolean DEBUG = false;
+
+ private static final int MIN_WIDTH = 100;
+ private static final int MIN_HEIGHT = 100;
+ private static final int MAX_WIDTH = 4096;
+ private static final int MAX_HEIGHT = 4096;
+
+ private static final Pattern SETTING_PATTERN =
+ Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
+
+ private final Handler mUiHandler;
+ private final ArrayList<OverlayDisplayHandle> mOverlays =
+ new ArrayList<OverlayDisplayHandle>();
+ private String mCurrentOverlaySetting = "";
+
+ public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, Handler uiHandler) {
+ super(syncRoot, context, handler, listener, TAG);
+ mUiHandler = uiHandler;
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+ pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
+ pw.println("mOverlays: size=" + mOverlays.size());
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dumpLocked(pw);
+ }
+ }
+
+ @Override
+ public void registerLocked() {
+ super.registerLocked();
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true,
+ new ContentObserver(getHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (getSyncRoot()) {
+ updateOverlayDisplayDevicesLocked();
+ }
+ }
+ });
+ updateOverlayDisplayDevicesLocked();
+ }
+
+ private void updateOverlayDisplayDevicesLocked() {
+ String value = Settings.System.getString(getContext().getContentResolver(),
+ Settings.Secure.OVERLAY_DISPLAY_DEVICES);
+ if (value == null) {
+ value = "";
+ }
+
+ if (value.equals(mCurrentOverlaySetting)) {
+ return;
+ }
+ mCurrentOverlaySetting = value;
+
+ if (!mOverlays.isEmpty()) {
+ Slog.i(TAG, "Dismissing all overlay display devices.");
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dismissLocked();
+ }
+ mOverlays.clear();
+ }
+
+ int count = 0;
+ for (String part : value.split(";")) {
+ Matcher matcher = SETTING_PATTERN.matcher(part);
+ if (matcher.matches()) {
+ if (count >= 4) {
+ Slog.w(TAG, "Too many overlay display devices specified: " + value);
+ break;
+ }
+ try {
+ int width = Integer.parseInt(matcher.group(1), 10);
+ int height = Integer.parseInt(matcher.group(2), 10);
+ int densityDpi = Integer.parseInt(matcher.group(3), 10);
+ if (width >= MIN_WIDTH && width <= MAX_WIDTH
+ && height >= MIN_HEIGHT && height <= MAX_HEIGHT
+ && densityDpi >= DisplayMetrics.DENSITY_LOW
+ && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
+ int number = ++count;
+ String name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_name,
+ number);
+ int gravity = chooseOverlayGravity(number);
+
+ Slog.i(TAG, "Showing overlay display device #" + number
+ + ": name=" + name + ", width=" + width + ", height=" + height
+ + ", densityDpi=" + densityDpi);
+
+ mOverlays.add(new OverlayDisplayHandle(name,
+ width, height, densityDpi, gravity));
+ continue;
+ }
+ } catch (NumberFormatException ex) {
+ }
+ } else if (part.isEmpty()) {
+ continue;
+ }
+ Slog.w(TAG, "Malformed overlay display devices setting: " + value);
+ }
+ }
+
+ private static int chooseOverlayGravity(int overlayNumber) {
+ switch (overlayNumber) {
+ case 1:
+ return Gravity.TOP | Gravity.LEFT;
+ case 2:
+ return Gravity.BOTTOM | Gravity.RIGHT;
+ case 3:
+ return Gravity.TOP | Gravity.RIGHT;
+ case 4:
+ default:
+ return Gravity.BOTTOM | Gravity.LEFT;
+ }
+ }
+
+ private final class OverlayDisplayDevice extends DisplayDevice {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final float mRefreshRate;
+ private final int mDensityDpi;
+
+ private SurfaceTexture mSurfaceTexture;
+ private boolean mSurfaceTextureChanged;
+
+ private DisplayDeviceInfo mInfo;
+
+ public OverlayDisplayDevice(IBinder displayToken, String name,
+ int width, int height, float refreshRate, int densityDpi) {
+ super(OverlayDisplayAdapter.this, displayToken);
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mRefreshRate = refreshRate;
+ mDensityDpi = densityDpi;
+ }
+
+ public void setSurfaceTextureLocked(SurfaceTexture surfaceTexture) {
+ if (surfaceTexture != mSurfaceTexture) {
+ mSurfaceTexture = surfaceTexture;
+ mSurfaceTextureChanged = true;
+ sendTraversalRequestLocked();
+ }
+ }
+
+ @Override
+ public void performTraversalInTransactionLocked() {
+ if (mSurfaceTextureChanged) {
+ setSurfaceTextureInTransactionLocked(mSurfaceTexture);
+ mSurfaceTextureChanged = false;
+ }
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = mRefreshRate;
+ mInfo.densityDpi = mDensityDpi;
+ mInfo.xDpi = mDensityDpi;
+ mInfo.yDpi = mDensityDpi;
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ }
+ return mInfo;
+ }
+ }
+
+ /**
+ * Functions as a handle for overlay display devices which are created and
+ * destroyed asynchronously.
+ *
+ * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ */
+ private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+
+ private OverlayDisplayWindow mWindow;
+ private OverlayDisplayDevice mDevice;
+
+ public OverlayDisplayHandle(String name,
+ int width, int height, int densityDpi, int gravity) {
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+
+ mUiHandler.post(mShowRunnable);
+ }
+
+ public void dismissLocked() {
+ mUiHandler.removeCallbacks(mShowRunnable);
+ mUiHandler.post(mDismissRunnable);
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
+ synchronized (getSyncRoot()) {
+ IBinder displayToken = Surface.createDisplay(mName);
+ mDevice = new OverlayDisplayDevice(displayToken, mName,
+ mWidth, mHeight, refreshRate, mDensityDpi);
+ mDevice.setSurfaceTextureLocked(surfaceTexture);
+
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowDestroyed() {
+ synchronized (getSyncRoot()) {
+ if (mDevice != null) {
+ mDevice.setSurfaceTextureLocked(null);
+
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" " + mName + ": ");
+ pw.println(" mWidth=" + mWidth);
+ pw.println(" mHeight=" + mHeight);
+ pw.println(" mDensityDpi=" + mDensityDpi);
+ pw.println(" mGravity=" + mGravity);
+
+ // Try to dump the window state.
+ // This call may hang if the UI thread is waiting to acquire our lock so
+ // we use a short timeout to recover just in case.
+ if (mWindow != null) {
+ final StringWriter sw = new StringWriter();
+ final OverlayDisplayWindow window = mWindow;
+ if (mUiHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ PrintWriter lpw = new PrintWriter(sw);
+ window.dump(lpw);
+ lpw.close();
+ }
+ }, 200)) {
+ for (String line : sw.toString().split("\n")) {
+ pw.println(line);
+ }
+ } else {
+ pw.println(" ... timed out while attempting to dump window state");
+ }
+ }
+ }
+
+ // Runs on the UI thread.
+ private final Runnable mShowRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
+ mName, mWidth, mHeight, mDensityDpi, mGravity,
+ OverlayDisplayHandle.this);
+ window.show();
+
+ synchronized (getSyncRoot()) {
+ mWindow = window;
+ }
+ }
+ };
+
+ // Runs on the UI thread.
+ private final Runnable mDismissRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window;
+ synchronized (getSyncRoot()) {
+ window = mWindow;
+ mWindow = null;
+ }
+
+ if (window != null) {
+ window.dismiss();
+ }
+ }
+ };
+ }
+}
diff --git a/services/java/com/android/server/display/OverlayDisplayWindow.java b/services/java/com/android/server/display/OverlayDisplayWindow.java
new file mode 100644
index 0000000..6adfa0f
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayWindow.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.display.DisplayManager;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.TextureView;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.TextureView.SurfaceTextureListener;
+import android.widget.TextView;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages an overlay window on behalf of {@link OverlayDisplayAdapter}.
+ * <p>
+ * This object must only be accessed on the UI thread.
+ * No locks are held by this object and locks must not be held while making called into it.
+ * </p>
+ */
+final class OverlayDisplayWindow {
+ private static final String TAG = "OverlayDisplayWindow";
+ private static final boolean DEBUG = false;
+
+ private final float INITIAL_SCALE = 0.5f;
+ private final float MIN_SCALE = 0.3f;
+ private final float MAX_SCALE = 1.0f;
+ private final float WINDOW_ALPHA = 0.8f;
+
+ // When true, disables support for moving and resizing the overlay.
+ // The window is made non-touchable, which makes it possible to
+ // directly interact with the content underneath.
+ private final boolean DISABLE_MOVE_AND_RESIZE = false;
+
+ private final Context mContext;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+ private final Listener mListener;
+ private final String mTitle;
+
+ private final DisplayManager mDisplayManager;
+ private final WindowManager mWindowManager;
+
+
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ private View mWindowContent;
+ private WindowManager.LayoutParams mWindowParams;
+ private TextureView mTextureView;
+ private TextView mTitleTextView;
+
+ private GestureDetector mGestureDetector;
+ private ScaleGestureDetector mScaleGestureDetector;
+
+ private boolean mWindowVisible;
+ private int mWindowX;
+ private int mWindowY;
+ private float mWindowScale;
+
+ private float mLiveTranslationX;
+ private float mLiveTranslationY;
+ private float mLiveScale = 1.0f;
+
+ public OverlayDisplayWindow(Context context, String name,
+ int width, int height, int densityDpi, int gravity, Listener listener) {
+ mContext = context;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+ mListener = listener;
+ mTitle = context.getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_title,
+ mName, mWidth, mHeight, mDensityDpi);
+
+ mDisplayManager = (DisplayManager)context.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE);
+
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ updateDefaultDisplayInfo();
+
+ createWindow();
+ }
+
+ public void show() {
+ if (!mWindowVisible) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ if (!updateDefaultDisplayInfo()) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ return;
+ }
+
+ clearLiveState();
+ updateWindowParams();
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ mWindowVisible = true;
+ }
+ }
+
+ public void dismiss() {
+ if (mWindowVisible) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mWindowManager.removeView(mWindowContent);
+ mWindowVisible = false;
+ }
+ }
+
+ public void relayout() {
+ if (mWindowVisible) {
+ updateWindowParams();
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" mWindowVisible=" + mWindowVisible);
+ pw.println(" mWindowX=" + mWindowX);
+ pw.println(" mWindowY=" + mWindowY);
+ pw.println(" mWindowScale=" + mWindowScale);
+ pw.println(" mWindowParams=" + mWindowParams);
+ if (mTextureView != null) {
+ pw.println(" mTextureView.getScaleX()=" + mTextureView.getScaleX());
+ pw.println(" mTextureView.getScaleY()=" + mTextureView.getScaleY());
+ }
+ pw.println(" mLiveTranslationX=" + mLiveTranslationX);
+ pw.println(" mLiveTranslationY=" + mLiveTranslationY);
+ pw.println(" mLiveScale=" + mLiveScale);
+ }
+
+ private boolean updateDefaultDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.w(TAG, "Cannot show overlay display because there is no "
+ + "default display upon which to show it.");
+ return false;
+ }
+ return true;
+ }
+
+ private void createWindow() {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ mWindowContent = inflater.inflate(
+ com.android.internal.R.layout.overlay_display_window, null);
+ mWindowContent.setOnTouchListener(mOnTouchListener);
+
+ mTextureView = (TextureView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_texture);
+ mTextureView.setPivotX(0);
+ mTextureView.setPivotY(0);
+ mTextureView.getLayoutParams().width = mWidth;
+ mTextureView.getLayoutParams().height = mHeight;
+ mTextureView.setOpaque(false);
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+ mTitleTextView = (TextView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_title);
+ mTitleTextView.setText(mTitle);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ if (DISABLE_MOVE_AND_RESIZE) {
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ }
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
+ mWindowParams.alpha = WINDOW_ALPHA;
+ mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ mWindowParams.setTitle(mTitle);
+
+ mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
+ mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
+
+ // Set the initial position and scale.
+ // The position and scale will be clamped when the display is first shown.
+ mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
+ 0 : mDefaultDisplayInfo.logicalWidth;
+ mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
+ 0 : mDefaultDisplayInfo.logicalHeight;
+ mWindowScale = INITIAL_SCALE;
+ }
+
+ private void updateWindowParams() {
+ float scale = mWindowScale * mLiveScale;
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalWidth / mWidth);
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalHeight / mHeight);
+ scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
+
+ float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
+ int width = (int)(mWidth * scale);
+ int height = (int)(mHeight * scale);
+ int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
+ int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
+ x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width));
+ y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height));
+
+ if (DEBUG) {
+ Slog.d(TAG, "updateWindowParams: scale=" + scale
+ + ", offsetScale=" + offsetScale
+ + ", x=" + x + ", y=" + y
+ + ", width=" + width + ", height=" + height);
+ }
+
+ mTextureView.setScaleX(scale);
+ mTextureView.setScaleY(scale);
+
+ mWindowParams.x = x;
+ mWindowParams.y = y;
+ mWindowParams.width = width;
+ mWindowParams.height = height;
+ }
+
+ private void saveWindowParams() {
+ mWindowX = mWindowParams.x;
+ mWindowY = mWindowParams.y;
+ mWindowScale = mTextureView.getScaleX();
+ clearLiveState();
+ }
+
+ private void clearLiveState() {
+ mLiveTranslationX = 0f;
+ mLiveTranslationY = 0f;
+ mLiveScale = 1.0f;
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ if (updateDefaultDisplayInfo()) {
+ relayout();
+ } else {
+ dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ dismiss();
+ }
+ }
+ };
+
+ private final SurfaceTextureListener mSurfaceTextureListener =
+ new SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ mListener.onWindowCreated(surface, mDefaultDisplayInfo.refreshRate);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mListener.onWindowDestroyed();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+ };
+
+ private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ // Work in screen coordinates.
+ final float oldX = event.getX();
+ final float oldY = event.getY();
+ event.setLocation(event.getRawX(), event.getRawY());
+
+ mGestureDetector.onTouchEvent(event);
+ mScaleGestureDetector.onTouchEvent(event);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ saveWindowParams();
+ break;
+ }
+
+ // Revert to window coordinates.
+ event.setLocation(oldX, oldY);
+ return true;
+ }
+ };
+
+ private final GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ mLiveTranslationX -= distanceX;
+ mLiveTranslationY -= distanceY;
+ relayout();
+ return true;
+ }
+ };
+
+ private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mLiveScale *= detector.getScaleFactor();
+ relayout();
+ return true;
+ }
+ };
+
+ /**
+ * Watches for significant changes in the overlay display window lifecycle.
+ */
+ public interface Listener {
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate);
+ public void onWindowDestroyed();
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java b/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java
deleted file mode 100644
index 9531acb..0000000
--- a/services/java/com/android/server/display/SurfaceFlingerDisplayAdapter.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.server.display;
-
-import android.content.Context;
-
-/**
- * A display adapter for the displays managed by Surface Flinger.
- */
-public final class SurfaceFlingerDisplayAdapter extends DisplayAdapter {
- private final Context mContext;
- private final SurfaceFlingerDisplayDevice mDefaultDisplayDevice;
-
- private static native void nativeGetDefaultDisplayDeviceInfo(DisplayDeviceInfo outInfo);
-
- public SurfaceFlingerDisplayAdapter(Context context) {
- mContext = context;
- mDefaultDisplayDevice = new SurfaceFlingerDisplayDevice();
- }
-
- @Override
- public String getName() {
- return "SurfaceFlingerDisplayAdapter";
- }
-
- @Override
- public void register(Listener listener) {
- listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
- }
-
- private final class SurfaceFlingerDisplayDevice extends DisplayDevice {
- @Override
- public DisplayAdapter getAdapter() {
- return SurfaceFlingerDisplayAdapter.this;
- }
-
- @Override
- public void getInfo(DisplayDeviceInfo outInfo) {
- outInfo.name = mContext.getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display);
- nativeGetDefaultDisplayDeviceInfo(outInfo);
- }
- }
-}
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 29c68eb..fd4f5fc 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -282,29 +282,28 @@
nativeReloadDeviceAliases(mPtr);
}
- public void setDisplaySize(int displayId, int width, int height,
- int externalWidth, int externalHeight) {
- if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
+ public void setDisplaySize(int displayId, int width, int height) {
+ if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Invalid display id or dimensions.");
}
if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height
- + " external size " + externalWidth + "x" + externalHeight);
+ Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
}
- nativeSetDisplaySize(mPtr, displayId, width, height, externalWidth, externalHeight);
+ // FIXME: external size is deprecated
+ nativeSetDisplaySize(mPtr, displayId, width, height, 1280, 720);
}
-
- public void setDisplayOrientation(int displayId, int rotation, int externalRotation) {
+
+ public void setDisplayOrientation(int displayId, int rotation) {
if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
throw new IllegalArgumentException("Invalid rotation.");
}
if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation
- + " external rotation " + externalRotation);
+ Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation);
}
- nativeSetDisplayOrientation(mPtr, displayId, rotation, externalRotation);
+ // FIXME: external rotation is deprecated
+ nativeSetDisplayOrientation(mPtr, displayId, rotation, Surface.ROTATION_0);
}
/**
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index c2c0a71..bb11fe7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -47,6 +47,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Telephony.Carriers;
@@ -1092,7 +1093,7 @@
// send an intent to notify that the GPS is receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
updateStatus(LocationProvider.AVAILABLE, mSvCount);
}
@@ -1150,7 +1151,7 @@
// send an intent to notify that the GPS has been enabled or disabled.
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
}
@@ -1202,7 +1203,7 @@
// send an intent to notify that the GPS is no longer receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
}
}
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
index 541650e..f2d6745 100644
--- a/services/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -41,6 +41,7 @@
import com.android.internal.net.VpnProfile;
import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService;
+import com.android.server.EventLogTags;
import com.android.server.connectivity.Vpn;
/**
@@ -55,6 +56,7 @@
private static final int MAX_ERROR_COUNT = 4;
private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
+ private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
private final Context mContext;
private final INetworkManagementService mNetService;
@@ -84,9 +86,9 @@
mVpn = Preconditions.checkNotNull(vpn);
mProfile = Preconditions.checkNotNull(profile);
- final Intent intent = new Intent(ACTION_LOCKDOWN_RESET);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
+ resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
}
private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
@@ -115,18 +117,24 @@
final boolean egressChanged = egressProp == null
|| !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
if (egressDisconnected || egressChanged) {
- clearSourceRules();
+ clearSourceRulesLocked();
mAcceptedEgressIface = null;
mVpn.stopLegacyVpn();
}
if (egressDisconnected) return;
+ final int egressType = egressInfo.getType();
+ if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
+ EventLogTags.writeLockdownVpnError(egressType);
+ }
+
if (mErrorCount > MAX_ERROR_COUNT) {
showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
} else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
if (mProfile.isValidLockdownProfile()) {
Slog.d(TAG, "Active network connected; starting VPN");
+ EventLogTags.writeLockdownVpnConnecting(egressType);
showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
mAcceptedEgressIface = egressProp.getInterfaceName();
@@ -147,10 +155,11 @@
}
Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr);
+ EventLogTags.writeLockdownVpnConnected(egressType);
showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
try {
- clearSourceRules();
+ clearSourceRulesLocked();
mNetService.setFirewallInterfaceRule(iface, true);
mNetService.setFirewallEgressSourceRule(sourceAddr, true);
@@ -167,7 +176,13 @@
}
public void init() {
- Slog.d(TAG, "init()");
+ synchronized (mStateLock) {
+ initLocked();
+ }
+ }
+
+ private void initLocked() {
+ Slog.d(TAG, "initLocked()");
mVpn.setEnableNotifications(false);
@@ -188,7 +203,13 @@
}
public void shutdown() {
- Slog.d(TAG, "shutdown()");
+ synchronized (mStateLock) {
+ shutdownLocked();
+ }
+ }
+
+ private void shutdownLocked() {
+ Slog.d(TAG, "shutdownLocked()");
mAcceptedEgressIface = null;
mErrorCount = 0;
@@ -200,7 +221,7 @@
} catch (RemoteException e) {
throw new RuntimeException("Problem setting firewall rules", e);
}
- clearSourceRules();
+ clearSourceRulesLocked();
hideNotification();
mContext.unregisterReceiver(mResetReceiver);
@@ -208,15 +229,15 @@
}
public void reset() {
- // cycle tracker, reset error count, and trigger retry
- shutdown();
- init();
synchronized (mStateLock) {
+ // cycle tracker, reset error count, and trigger retry
+ shutdownLocked();
+ initLocked();
handleStateChangedLocked();
}
}
- private void clearSourceRules() {
+ private void clearSourceRulesLocked() {
try {
if (mAcceptedIface != null) {
mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a7cba5a..46bddc4 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -788,11 +788,13 @@
}
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
- packageName, tag, 0x0, builder.getNotification(), idReceived);
+ packageName, tag, 0x0, builder.getNotification(), idReceived,
+ UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -822,11 +824,12 @@
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, tag,
- 0x0, builder.getNotification(), idReceived);
+ 0x0, builder.getNotification(), idReceived, UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -835,10 +838,11 @@
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
mNotifManager.cancelNotificationWithTag(
- packageName, tag, 0x0);
+ packageName, tag, 0x0, UserHandle.USER_OWNER);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1231,7 +1235,7 @@
if (mRestrictBackground) {
final Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- mContext.sendBroadcast(broadcast);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
}
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index ffe2a3d..f2d2fb7 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -99,6 +99,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.telephony.PhoneStateListener;
@@ -989,7 +990,8 @@
// finally, dispatch updated event to any listeners
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY);
+ mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
+ READ_NETWORK_USAGE_HISTORY);
}
/**
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index b84e25a..55da11f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -208,8 +208,7 @@
/**
* Whether verification is enabled by default.
*/
- // STOPSHIP: change this to true
- private static final boolean DEFAULT_VERIFY_ENABLE = false;
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
/**
* The default maximum time to wait for the verification agent to return in
@@ -710,20 +709,56 @@
res.removedInfo.sendBroadcast(false, true);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
+ // Determine the set of users who are adding this
+ // package for the first time vs. those who are seeing
+ // an update.
+ int[] firstUsers;
+ int[] updateUsers = new int[0];
+ if (res.origUsers == null || res.origUsers.length == 0) {
+ firstUsers = res.newUsers;
+ } else {
+ firstUsers = new int[0];
+ for (int i=0; i<res.newUsers.length; i++) {
+ int user = res.newUsers[i];
+ boolean isNew = true;
+ for (int j=0; j<res.origUsers.length; j++) {
+ if (res.origUsers[j] == user) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ int[] newFirst = new int[firstUsers.length+1];
+ System.arraycopy(firstUsers, 0, newFirst, 0,
+ firstUsers.length);
+ newFirst[firstUsers.length] = user;
+ firstUsers = newFirst;
+ } else {
+ int[] newUpdate = new int[updateUsers.length+1];
+ System.arraycopy(updateUsers, 0, newUpdate, 0,
+ updateUsers.length);
+ newUpdate[updateUsers.length] = user;
+ updateUsers = newUpdate;
+ }
+ }
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ res.pkg.applicationInfo.packageName,
+ extras, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null, res.users);
+ extras, null, null, updateUsers);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null, res.users);
+ extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null, res.users);
+ res.pkg.applicationInfo.packageName, null, updateUsers);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -815,11 +850,16 @@
+ args.packageURI.toString());
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+ broadcastPackageVerified(verificationId, args.packageURI,
+ PackageManager.VERIFICATION_ALLOW);
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
Slog.e(TAG, "Could not contact the ContainerService");
}
+ } else {
+ broadcastPackageVerified(verificationId, args.packageURI,
+ PackageManager.VERIFICATION_REJECT);
}
processPendingInstall(args, ret);
@@ -848,6 +888,8 @@
int ret;
if (state.isInstallAllowed()) {
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ broadcastPackageVerified(verificationId, args.packageURI,
+ response.code);
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
@@ -1597,6 +1639,7 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package info");
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1639,6 +1682,7 @@
@Override
public int getPackageUid(String packageName, int userId) {
if (!sUserManager.exists(userId)) return -1;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get package uid");
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1782,7 +1826,7 @@
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.sourceDir = ps.codePathString;
pkg.applicationInfo.dataDir =
- getDataPathForPackage(ps.pkg.packageName, 0).getPath();
+ getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
// pkg.mSetEnabled = ps.getEnabled(userId);
@@ -1795,6 +1839,7 @@
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get application info");
// writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1874,6 +1919,7 @@
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -1894,6 +1940,7 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -1911,6 +1958,7 @@
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -1928,6 +1976,7 @@
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -2029,6 +2078,34 @@
return PackageManager.PERMISSION_DENIED;
}
+ /**
+ * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+ * @param message the message to log on security exception
+ * @return
+ */
+ private void enforceCrossUserPermission(int callingUid, int userId,
+ boolean requireFullPermission, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (userId == UserHandle.getUserId(callingUid)) return;
+ if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (requireFullPermission) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } else {
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ } catch (SecurityException se) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, message);
+ }
+ }
+ }
+ }
+
private BasePermission findPermissionTreeLP(String permName) {
for(BasePermission bp : mSettings.mPermissionTrees.values()) {
if (permName.startsWith(bp.name) &&
@@ -2361,6 +2438,7 @@
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
@@ -2502,6 +2580,7 @@
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities");
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2541,6 +2620,8 @@
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
+ "query intent activity options");
final String resultsAction = intent.getAction();
List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
@@ -5278,6 +5359,7 @@
return pkgs.get(0);
}
}
+ mSettings.mPackagesToBeCleaned.remove(userId);
}
// Move on to the next user to clean.
long ident = Binder.clearCallingIdentity();
@@ -5343,8 +5425,10 @@
public void onEvent(int event, String path) {
String removedPackage = null;
int removedUid = -1;
+ int[] removedUsers = null;
String addedPackage = null;
int addedUid = -1;
+ int[] addedUsers = null;
// TODO post a message to the handler to obtain serial ordering
synchronized (mInstallLock) {
@@ -5373,6 +5457,15 @@
// reader
synchronized (mPackages) {
p = mAppDirs.get(fullPathStr);
+ if (p != null) {
+ PackageSetting ps = mSettings.mPackages.get(p.applicationInfo.packageName);
+ if (ps != null) {
+ removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+ } else {
+ removedUsers = sUserManager.getUserIds();
+ }
+ }
+ addedUsers = sUserManager.getUserIds();
}
if ((event&REMOVE_EVENTS) != 0) {
if (p != null) {
@@ -5390,7 +5483,7 @@
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis(), null);
+ System.currentTimeMillis(), UserHandle.ALL);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -5419,13 +5512,13 @@
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, null);
+ extras, null, null, removedUsers);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, null);
+ extras, null, null, addedUsers);
}
}
@@ -5468,7 +5561,7 @@
if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
- user = Process.myUserHandle();
+ user = new UserHandle(UserHandle.getUserId(uid));
}
final int filteredFlags;
@@ -5531,6 +5624,10 @@
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can verify applications");
+
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCode, Binder.getCallingUid());
@@ -5542,17 +5639,23 @@
@Override
public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
long millisecondsToDelay) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can extend verification timeouts");
+
final PackageVerificationState state = mPendingVerification.get(id);
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCodeAtTimeout, Binder.getCallingUid());
- if ((millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT)
- || (millisecondsToDelay < 0)) {
- throw new IllegalArgumentException("millisecondsToDelay is out of bounds.");
+ if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
+ millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
+ }
+ if (millisecondsToDelay < 0) {
+ millisecondsToDelay = 0;
}
if ((verificationCodeAtTimeout != PackageManager.VERIFICATION_ALLOW)
- || (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) {
- throw new IllegalArgumentException("verificationCodeAtTimeout is unknown.");
+ && (verificationCodeAtTimeout != PackageManager.VERIFICATION_REJECT)) {
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
}
if ((state != null) && !state.timeoutExtended()) {
@@ -5565,6 +5668,17 @@
}
}
+ private void broadcastPackageVerified(int verificationId, Uri packageUri,
+ int verificationCode) {
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
+ intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode);
+
+ mContext.sendBroadcast(intent, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
+ }
+
private ComponentName matchComponentForVerifier(String packageName,
List<ResolveInfo> receivers) {
ActivityInfo targetReceiver = null;
@@ -7212,7 +7326,10 @@
class PackageInstalledInfo {
String name;
int uid;
- int[] users;
+ // The set of users that originally had this package installed.
+ int[] origUsers;
+ // The set of users that now have this package installed.
+ int[] newUsers;
PackageParser.Package pkg;
int returnCode;
PackageRemovedInfo removedInfo;
@@ -7362,7 +7479,7 @@
int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
| SCAN_UPDATE_TIME;
if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
- origUpdateTime, user) == null) {
+ origUpdateTime, null) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -7511,10 +7628,6 @@
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
res.name = pkgName;
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- if (ps != null) {
- res.users = ps.getInstalledUsers(sUserManager.getUserIds());
- }
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
@@ -7612,6 +7725,7 @@
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
@@ -7634,12 +7748,12 @@
installerPackageName, res);
} else {
installNewPackageLI(pkg, parseFlags, scanMode, args.user,
- installerPackageName,res);
+ installerPackageName, res);
}
synchronized (mPackages) {
- PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
- res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+ res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
}
@@ -7865,7 +7979,8 @@
if (outInfo != null) {
outInfo.removedPackage = packageName;
outInfo.removedUsers = deletedPs != null
- ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null;
+ ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)
+ : null;
}
}
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
@@ -8173,7 +8288,7 @@
final IPackageDataObserver observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
- checkValidCaller(Binder.getCallingUid(), userId);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "clear application data");
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
@@ -8405,7 +8520,7 @@
ComponentName[] set, ComponentName activity, int userId) {
// writer
int callingUid = Binder.getCallingUid();
- checkValidCaller(callingUid, userId);
+ enforceCrossUserPermission(callingUid, userId, true, "add preferred activity");
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@@ -8595,7 +8710,7 @@
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
@@ -8723,7 +8838,7 @@
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, true, "stop package");
// writer
synchronized (mPackages) {
if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission,
@@ -8744,7 +8859,7 @@
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "get enabled");
// reader
synchronized (mPackages) {
return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
@@ -8755,7 +8870,7 @@
public int getComponentEnabledSetting(ComponentName componentName, int userId) {
if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
- checkValidCaller(uid, userId);
+ enforceCrossUserPermission(uid, userId, false, "get component enabled");
// reader
synchronized (mPackages) {
return mSettings.getComponentEnabledSettingLPr(componentName, userId);
@@ -9519,7 +9634,8 @@
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Bundle extras, boolean ordered, boolean sticky,
+ int sendingUser) throws RemoteException {
Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
reportStatus ? 1 : 0, 1, keys);
mHandler.sendMessage(msg);
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 6d31f0e..d8f7345 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -210,17 +210,17 @@
return false;
}
- int[] getInstalledUsers(int[] users) {
+ int[] queryInstalledUsers(int[] users, boolean installed) {
int num = 0;
for (int user : users) {
- if (getInstalled(user)) {
+ if (getInstalled(user) == installed) {
num++;
}
}
int[] res = new int[num];
num = 0;
for (int user : users) {
- if (getInstalled(user)) {
+ if (getInstalled(user) == installed) {
res[num] = user;
num++;
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 68b594a..5f10d44 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -745,13 +745,12 @@
}
private File getUserPackagesStateFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions.xml");
+ return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
}
private File getUserPackagesStateBackupFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions-backup.xml");
+ return new File(Environment.getUserSystemDirectory(userId),
+ "package-restrictions-backup.xml");
}
void writeAllUsersPackageRestrictionsLPr() {
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 750aa72..a13c16e 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -23,6 +23,8 @@
import com.android.internal.util.FastXmlSerializer;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IStopUserCallback;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -33,6 +35,7 @@
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.Slog;
@@ -535,9 +538,10 @@
if (userInfo != null) {
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
- mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_BOOT_COMPLETED),
new UserHandle(userInfo.id));
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
}
return userInfo;
}
@@ -549,13 +553,36 @@
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
+ final UserInfo user;
+ synchronized (mPackagesLock) {
+ user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
+ }
+
+ int res;
+ try {
+ res = ActivityManagerNative.getDefault().stopUser(userHandle,
+ new IStopUserCallback.Stub() {
+ @Override
+ public void userStopped(int userId) {
+ finishRemoveUser(userId);
+ }
+ @Override
+ public void userStopAborted(int userId) {
+ }
+ });
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ return res == ActivityManager.USER_OP_SUCCESS;
+ }
+
+ void finishRemoveUser(int userHandle) {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
- final UserInfo user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
- return false;
- }
-
// Cleanup package manager settings
mPm.cleanUpUserLILPw(userHandle);
@@ -567,6 +594,7 @@
// Update the user list
writeUserListLocked();
updateUserIdsLocked();
+ removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
}
@@ -574,7 +602,17 @@
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
- return true;
+ }
+
+ private void removeDirectoryRecursive(File parent) {
+ if (parent.isDirectory()) {
+ String[] files = parent.list();
+ for (String filename : files) {
+ File child = new File(parent, filename);
+ removeDirectoryRecursive(child);
+ }
+ }
+ parent.delete();
}
@Override
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index cd211da..4f5561a 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -45,7 +45,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
/**
@@ -169,6 +168,9 @@
// The twilight service.
private final TwilightService mTwilight;
+ // The display manager.
+ private final DisplayManager mDisplayManager;
+
// The sensor manager.
private final SensorManager mSensorManager;
@@ -330,6 +332,7 @@
mLights = lights;
mTwilight = twilight;
mSensorManager = new SystemSensorManager(mHandler.getLooper());
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
final Resources resources = context.getResources();
mScreenBrightnessDimConfig = resources.getInteger(
@@ -475,7 +478,7 @@
private void initialize() {
final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
- Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
mPowerState = new DisplayPowerState(new ElectronBeam(display),
new PhotonicModulator(executor,
mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
@@ -980,7 +983,7 @@
}
};
- public void dump(PrintWriter pw) {
+ public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
pw.println("Display Controller Locked State:");
@@ -1000,33 +1003,12 @@
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
- if (Looper.myLooper() == mHandler.getLooper()) {
- dumpLocal(pw);
- } else {
- final StringWriter out = new StringWriter();
- final CountDownLatch latch = new CountDownLatch(1);
- Message msg = Message.obtain(mHandler, new Runnable() {
- @Override
- public void run() {
- PrintWriter localpw = new PrintWriter(out);
- try {
- dumpLocal(localpw);
- } finally {
- localpw.flush();
- latch.countDown();
- }
- }
- });
- msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
- try {
- latch.await();
- pw.print(out.toString());
- } catch (InterruptedException ex) {
- pw.println();
- pw.println("Failed to dump thread state due to interrupted exception!");
+ mHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ dumpLocal(pw);
}
- }
+ }, 1000);
}
private void dumpLocal(PrintWriter pw) {
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
index aad5a9a..0c68997 100644
--- a/services/java/com/android/server/power/ElectronBeam.java
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -481,8 +481,8 @@
try {
if (mSurface == null) {
try {
- mSurface = new Surface(mSurfaceSession, Process.myPid(),
- "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight,
+ mSurface = new Surface(mSurfaceSession,
+ "ElectronBeam", mDisplayWidth, mDisplayHeight,
PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
} catch (Surface.OutOfResourcesException ex) {
Slog.e(TAG, "Unable to create surface.", ex);
@@ -490,6 +490,7 @@
}
}
+ mSurface.setLayerStack(mDisplayLayerStack);
mSurface.setSize(mDisplayWidth, mDisplayHeight);
switch (mDisplayRotation) {
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index 75f8445..ce1e147 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -30,6 +30,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.util.EventLog;
import android.util.Slog;
@@ -361,7 +362,7 @@
}
if (ActivityManagerNative.isSystemReady()) {
- mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+ mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
mWakeUpBroadcastDone, mHandler, 0, null, null);
} else {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
@@ -403,7 +404,7 @@
}
if (ActivityManagerNative.isSystemReady()) {
- mContext.sendOrderedBroadcast(mScreenOffIntent, null,
+ mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
mGoToSleepBroadcastDone, mHandler, 0, null, null);
} else {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 6d68104..59d0954 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -1136,7 +1136,8 @@
private boolean isItBedTimeYetLocked() {
return mBootCompleted && !mStayOn
&& (mWakeLockSummary
- & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0
+ & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
+ | WAKE_LOCK_PROXIMITY_SCREEN_OFF)) == 0
&& (mUserActivitySummary
& (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0;
}
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index a3770d7..c7f7390 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -37,6 +37,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.os.SystemVibrator;
import android.os.storage.IMountService;
@@ -296,8 +297,8 @@
// First send the high-level shut down broadcast.
mActionDone = false;
- mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
- br, mHandler, 0, null, null);
+ mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
+ UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
index a3b45c7..1bb3a2c 100644
--- a/services/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -53,16 +53,15 @@
private final int BUFFER_SIZE = 4096;
private final Context mContext;
- private final Thread mThread;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
+ private Thread mThread;
private boolean mAdbEnabled = false;
private String mFingerprints;
private LocalSocket mSocket = null;
private OutputStream mOutputStream = null;
public UsbDebuggingManager(Context context) {
- mThread = new Thread(this);
mHandlerThread = new HandlerThread("UsbDebuggingHandler");
mHandlerThread.start();
mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
@@ -165,6 +164,7 @@
mAdbEnabled = true;
+ mThread = new Thread(UsbDebuggingManager.this);
mThread.start();
break;
@@ -181,8 +181,10 @@
} catch (Exception ex) {
}
+ mThread = null;
mOutputStream = null;
mSocket = null;
+ break;
case MESSAGE_ADB_ALLOW: {
String key = (String)msg.obj;
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index ddecf14..607ff39 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.os.SystemClock;
@@ -540,7 +541,7 @@
}
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void updateAudioSourceFunction() {
@@ -563,7 +564,7 @@
Slog.e(TAG, "could not open audio source PCM file", e);
}
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mAudioSourceEnabled = enabled;
}
}
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 9b3459b..a8453d3 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -35,6 +35,7 @@
import android.os.Binder;
import android.os.FileUtils;
import android.os.Process;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
@@ -546,7 +547,7 @@
}
// Send broadcast to running activity with registered intent
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
// Start activity with registered intent
resolveActivity(intent, matches, defaultPackage, device, null);
@@ -559,7 +560,7 @@
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
public void accessoryAttached(UsbAccessory accessory) {
@@ -586,7 +587,7 @@
Intent intent = new Intent(
UsbManager.ACTION_USB_ACCESSORY_DETACHED);
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index c25f010..c6b7e0d 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -234,10 +234,8 @@
return false;
}
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
- }
+ mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "AppWindowToken");
clearAnimation();
animating = false;
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index 64d2602..5b77b20 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -43,14 +43,15 @@
int w = r-l;
int h = b-t;
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- surface = new WindowStateAnimator.SurfaceTrace(session, 0, "BlackSurface("
- + l + ", " + t + ")", layerStack,
- w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ surface = new WindowStateAnimator.SurfaceTrace(session, "BlackSurface("
+ + l + ", " + t + ")",
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- surface = new Surface(session, 0, "BlackSurface", layerStack,
- w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ surface = new Surface(session, "BlackSurface",
+ w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
surface.setAlpha(1);
+ surface.setLayerStack(layerStack);
surface.setLayer(layer);
surface.show();
if (WindowManagerService.SHOW_TRANSACTIONS ||
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 81daac6..87a2880 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -43,20 +43,21 @@
if (mDimSurface == null) {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0,
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
"DimAnimator",
- layerStack, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- mDimSurface = new Surface(session, 0,
- "DimAnimator",
- layerStack, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ mDimSurface = new Surface(session, "DimAnimator",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
} catch (Exception e) {
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
@@ -211,5 +212,12 @@
mDimHeight = dimHeight;
mDimTarget = dimTarget;
}
+
+ Parameters(Parameters o) {
+ mDimWinAnimator = o.mDimWinAnimator;
+ mDimWidth = o.mDimWidth;
+ mDimHeight = o.mDimHeight;
+ mDimTarget = o.mDimTarget;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
index 4ab8ce1..ddbd70d 100644
--- a/services/java/com/android/server/wm/DimSurface.java
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -34,20 +34,21 @@
if (mDimSurface == null) {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mDimSurface = new WindowStateAnimator.SurfaceTrace(session, 0,
+ mDimSurface = new WindowStateAnimator.SurfaceTrace(session,
"DimSurface",
- layerStack, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
} else {
- mDimSurface = new Surface(session, 0,
- "DimSurface",
- layerStack, 16, 16, PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
+ mDimSurface = new Surface(session, "DimSurface",
+ 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM | Surface.HIDDEN);
}
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" DIM " + mDimSurface + ": CREATE");
+ mDimSurface.setLayerStack(layerStack);
mDimSurface.setAlpha(0.0f);
+ mDimSurface.show();
} catch (Exception e) {
Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 6e5bbcb..0ea051f 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,8 +16,10 @@
package com.android.server.wm;
+import android.os.RemoteCallbackList;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.IDisplayContentChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -41,6 +43,11 @@
* from mDisplayWindows; */
private WindowList mWindows = new WindowList();
+ // Specification for magnifying the display content.
+ MagnificationSpec mMagnificationSpec;
+
+ // Callback for observing content changes on a display.
+ RemoteCallbackList<IDisplayContentChangeListener> mDisplayContentChangeListeners;
// This protects the following display size properties, so that
// getDisplaySize() doesn't need to acquire the global lock. This is
@@ -62,10 +69,16 @@
final DisplayInfo mDisplayInfo = new DisplayInfo();
final Display mDisplay;
+ // Accessed directly by all users.
+ boolean layoutNeeded;
+ int pendingLayoutChanges;
+ final boolean isDefaultDisplay;
+
DisplayContent(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
display.getDisplayInfo(mDisplayInfo);
+ isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
}
int getDisplayId() {
@@ -81,6 +94,7 @@
}
DisplayInfo getDisplayInfo() {
+ // TODO: Add a listener for changes to Display and update mDisplayInfo when appropriate.
return mDisplayInfo;
}
@@ -106,6 +120,8 @@
pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+ pw.print(" layoutNeeded="); pw.println(layoutNeeded);
+ pw.print("magnificationSpec="); pw.println(mMagnificationSpec.toString());
pw.println();
}
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 3fcf680..dc52fcf 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -23,13 +23,14 @@
import android.content.ClipData;
import android.content.ClipDescription;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.DisplayInfo;
+import android.view.Display;
import android.view.DragEvent;
import android.view.InputChannel;
import android.view.Surface;
@@ -59,7 +60,7 @@
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
- DisplayContent mDisplayContent;
+ Display mDisplay;
private final Region mTmpRegion = new Region();
@@ -87,10 +88,10 @@
}
/**
- * @param displayContent The display parameters associated with the window being dragged.
+ * @param display The Display that the window being dragged is on.
*/
- void register(DisplayContent displayContent) {
- mDisplayContent = displayContent;
+ void register(Display display) {
+ mDisplay = display;
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
if (mClientChannel != null) {
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
@@ -108,7 +109,7 @@
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
- mDisplayContent.getDisplayId());
+ mDisplay.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = getDragLayerLw();
@@ -132,9 +133,10 @@
// The drag window covers the entire display
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
- DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- mDragWindowHandle.frameRight = displayInfo.logicalWidth;
- mDragWindowHandle.frameBottom = displayInfo.logicalHeight;
+ Point p = new Point();
+ mDisplay.getRealSize(p);
+ mDragWindowHandle.frameRight = p.x;
+ mDragWindowHandle.frameBottom = p.y;
// Pause rotations before a drag.
if (WindowManagerService.DEBUG_ORIENTATION) {
@@ -187,7 +189,7 @@
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
- final WindowList windows = mDisplayContent.getWindowList();
+ final WindowList windows = mService.getWindowList(mDisplay);
final int N = windows.size();
for (int i = 0; i < N; i++) {
sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
@@ -390,7 +392,7 @@
final int x = (int) xf;
final int y = (int) yf;
- final WindowList windows = mDisplayContent.getWindowList();
+ final WindowList windows = mService.getWindowList(mDisplay);
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
WindowState child = windows.get(i);
diff --git a/services/java/com/android/server/wm/MagnificationSpec.java b/services/java/com/android/server/wm/MagnificationSpec.java
new file mode 100644
index 0000000..31aae66
--- /dev/null
+++ b/services/java/com/android/server/wm/MagnificationSpec.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server.wm;
+
+public class MagnificationSpec {
+ public float mScale = 1.0f;
+ public float mOffsetX;
+ public float mOffsetY;
+
+ public void initialize(float scale, float offsetX, float offsetY) {
+ mScale = scale;
+ mOffsetX = offsetX;
+ mOffsetY = offsetY;
+ }
+
+ public boolean isNop() {
+ return mScale == 1.0f && mOffsetX == 0 && mOffsetY == 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<scale:");
+ builder.append(mScale);
+ builder.append(",offsetX:");
+ builder.append(mOffsetX);
+ builder.append(",offsetY:");
+ builder.append(mOffsetY);
+ builder.append(">");
+ return builder.toString();
+ }
+}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 7679413..7c7d4b1 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -215,12 +215,12 @@
try {
try {
if (WindowManagerService.DEBUG_SURFACE_TRACE) {
- mSurface = new SurfaceTrace(session, 0, "FreezeSurface",
- mDisplay.getLayerStack(), mWidth, mHeight,
+ mSurface = new SurfaceTrace(session, "FreezeSurface",
+ mWidth, mHeight,
PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
} else {
- mSurface = new Surface(session, 0, "FreezeSurface",
- mDisplay.getLayerStack(), mWidth, mHeight,
+ mSurface = new Surface(session, "FreezeSurface",
+ mWidth, mHeight,
PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
}
if (!mSurface.isValid()) {
@@ -228,6 +228,7 @@
mSurface = null;
return;
}
+ mSurface.setLayerStack(mDisplay.getLayerStack());
mSurface.setLayer(FREEZE_LAYER + 1);
mSurface.setAlpha(0);
mSurface.show();
@@ -239,7 +240,7 @@
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" FREEZE " + mSurface + ": CREATE");
- setRotation(originalRotation);
+ setRotationInTransaction(originalRotation);
} finally {
if (!inTransaction) {
Surface.closeTransaction();
@@ -259,7 +260,7 @@
return delta;
}
- void setSnapshotTransform(Matrix matrix, float alpha) {
+ private void setSnapshotTransformInTransaction(Matrix matrix, float alpha) {
if (mSurface != null) {
matrix.getValues(mTmpFloats);
mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
@@ -302,7 +303,7 @@
}
// Must be called while in a transaction.
- private void setRotation(int rotation) {
+ private void setRotationInTransaction(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -312,13 +313,13 @@
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
- setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
+ setSnapshotTransformInTransaction(mSnapshotInitialMatrix, 1.0f);
}
// Must be called while in a transaction.
- public boolean setRotation(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation, SurfaceSession session,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
- setRotation(rotation);
+ setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
return startAnimation(session, maxAnimationDuration, animationScale,
finalWidth, finalHeight, false);
@@ -514,16 +515,15 @@
WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
Surface.openTransaction();
-
- // Compute the transformation matrix that must be applied
- // the the black frame to make it stay in the initial position
- // before the new screen rotation. This is different than the
- // snapshot transformation because the snapshot is always based
- // of the native orientation of the screen, not the orientation
- // we were last in.
- createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
-
try {
+ // Compute the transformation matrix that must be applied
+ // the the black frame to make it stay in the initial position
+ // before the new screen rotation. This is different than the
+ // snapshot transformation because the snapshot is always based
+ // of the native orientation of the screen, not the orientation
+ // we were last in.
+ createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
+
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
@@ -843,7 +843,7 @@
return more;
}
- void updateSurfaces() {
+ void updateSurfacesInTransaction() {
if (!mStarted) {
return;
}
@@ -883,7 +883,7 @@
}
}
- setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
+ setSnapshotTransformInTransaction(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
public boolean stepAnimationLocked(long now) {
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 1ffbecc..16beeab 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -280,7 +280,7 @@
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
// the actual drag event dispatch stuff in the dragstate
- mService.mDragState.register(callingWin.mDisplayContent);
+ mService.mDragState.register(callingWin.mDisplayContent.getDisplay());
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
@@ -422,6 +422,17 @@
}
}
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ synchronized(mService.mWindowMap) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.onRectangleOnScreenRequested(token, rectangle, immediate);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/java/com/android/server/wm/StrictModeFlash.java b/services/java/com/android/server/wm/StrictModeFlash.java
index 775aa0f..90bbd08 100644
--- a/services/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/java/com/android/server/wm/StrictModeFlash.java
@@ -37,14 +37,16 @@
public StrictModeFlash(Display display, SurfaceSession session) {
try {
- mSurface = new Surface(session, 0, "StrictModeFlash", display.getLayerStack(),
- 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface = new Surface(session, "StrictModeFlash",
+ 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
} catch (Surface.OutOfResourcesException e) {
return;
}
+ mSurface.setLayerStack(display.getLayerStack());
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
mSurface.setPosition(0, 0);
+ mSurface.show();
mDrawNeeded = true;
}
diff --git a/services/java/com/android/server/wm/Watermark.java b/services/java/com/android/server/wm/Watermark.java
index 5901cc8..ac152c9 100644
--- a/services/java/com/android/server/wm/Watermark.java
+++ b/services/java/com/android/server/wm/Watermark.java
@@ -113,9 +113,9 @@
mTextPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
try {
- mSurface = new Surface(session, 0,
- "WatermarkSurface", mDisplay.getLayerStack(),
- 1, 1, PixelFormat.TRANSLUCENT, 0);
+ mSurface = new Surface(session, "WatermarkSurface",
+ 1, 1, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ mSurface.setLayerStack(mDisplay.getLayerStack());
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
mSurface.setPosition(0, 0);
mSurface.show();
@@ -174,4 +174,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 580f00d..72c6a51 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -16,12 +16,12 @@
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
-import com.android.internal.policy.impl.PhoneWindowManager;
import com.android.server.wm.WindowManagerService.AppWindowAnimParams;
import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams;
@@ -47,8 +47,10 @@
int mAdjResult;
- int mPendingLayoutChanges;
+ // Layout changes for individual Displays. Indexed by displayId.
+ SparseIntArray mPendingLayoutChanges = new SparseIntArray();
+ // TODO: Assign these from each iteration through DisplayContent. Only valid between loops.
/** Overall window dimensions */
int mDw, mDh;
@@ -97,7 +99,7 @@
static class AnimatorToLayoutParams {
boolean mUpdateQueued;
int mBulkUpdateParams;
- int mPendingLayoutChanges;
+ SparseIntArray mPendingLayoutChanges;
WindowState mWindowDetachedWallpaper;
}
/** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */
@@ -137,7 +139,7 @@
final AnimatorToLayoutParams animToLayout = mAnimToLayout;
synchronized (animToLayout) {
animToLayout.mBulkUpdateParams = mBulkUpdateParams;
- animToLayout.mPendingLayoutChanges = mPendingLayoutChanges;
+ animToLayout.mPendingLayoutChanges = mPendingLayoutChanges.clone();
animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper;
if (!animToLayout.mUpdateQueued) {
@@ -175,7 +177,7 @@
// Set the new DimAnimator params.
DimAnimator.Parameters dimParams = layoutToAnim.mDimParams;
if (dimParams == null) {
- mDimParams = dimParams;
+ mDimParams = null;
} else {
final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator;
@@ -186,7 +188,7 @@
if (newWinAnimator.mSurfaceShown &&
(existingDimWinAnimator == null || !existingDimWinAnimator.mSurfaceShown
|| existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- mDimParams = dimParams;
+ mDimParams = new DimAnimator.Parameters(dimParams);
}
}
@@ -214,7 +216,8 @@
if (!winAnimator.mLastHidden) {
winAnimator.hide();
mService.dispatchWallpaperVisibility(wallpaper, false);
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
}
}
token.hidden = true;
@@ -233,11 +236,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
- mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating " + appAnimator.mAppToken);
}
@@ -252,11 +252,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
- + " done", mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "exiting appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
}
@@ -302,10 +299,11 @@
if (wasAnimating && !winAnimator.mAnimating && mWallpaperTarget == win) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
@@ -315,10 +313,12 @@
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: " + win);
mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
mService.mFocusMayChange = true;
}
@@ -377,10 +377,11 @@
}
if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
}
@@ -390,10 +391,12 @@
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ mPendingLayoutChanges.put(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
}
}
@@ -536,14 +539,14 @@
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
// This will set mOrientationChangeComplete and cause a pass through layout.
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "testTokenMayBeDrawnLocked: freezingScreen");
} else {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
- mPendingLayoutChanges);
- }
-
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "testTokenMayBeDrawnLocked");
+
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
mAnimating |= appAnimator.showAllWindowsLocked();
@@ -557,12 +560,6 @@
private void performAnimationsLocked(final WinAnimatorList winAnimatorList) {
updateWindowsLocked(winAnimatorList);
updateWallpaperLocked(winAnimatorList);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mPendingActions |= WALLPAPER_ACTION_PENDING;
- }
-
- testTokenMayBeDrawnLocked();
}
// TODO(cmautner): Change the following comment when no longer locked on mWindowMap */
@@ -571,13 +568,8 @@
if (!mInitialized) {
return;
}
- for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
- animateLocked(mWinAnimatorLists.get(i));
- }
- }
- private void animateLocked(final WinAnimatorList winAnimatorList) {
- mPendingLayoutChanges = 0;
+ mPendingLayoutChanges.clear();
mCurrentTime = SystemClock.uptimeMillis();
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
@@ -586,23 +578,29 @@
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, ">>> OPEN TRANSACTION animateLocked");
Surface.openTransaction();
-
try {
updateWindowsAppsAndRotationAnimationsLocked();
- performAnimationsLocked(winAnimatorList);
- // THIRD LOOP: Update the surfaces of all windows.
+ for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
+ final WinAnimatorList winAnimatorList = mWinAnimatorLists.get(i);
- if (mScreenRotationAnimation != null) {
- mScreenRotationAnimation.updateSurfaces();
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ performAnimationsLocked(winAnimatorList);
+
+ final int N = winAnimatorList.size();
+ for (int j = 0; j < N; j++) {
+ winAnimatorList.get(j).prepareSurfaceLocked(true);
+ }
}
- final int N = winAnimatorList.size();
- for (int i = 0; i < N; i++) {
- winAnimatorList.get(i).prepareSurfaceLocked(true);
+ testTokenMayBeDrawnLocked();
+
+ if (mScreenRotationAnimation != null) {
+ mScreenRotationAnimation.updateSurfacesInTransaction();
}
if (mDimParams != null) {
@@ -629,9 +627,18 @@
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, "<<< CLOSE TRANSACTION animateLocked");
}
- if (mBulkUpdateParams != 0 || mPendingLayoutChanges != 0) {
+ for (int i = mPendingLayoutChanges.size() - 1; i >= 0; i--) {
+ if ((mPendingLayoutChanges.valueAt(i)
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mPendingActions |= WALLPAPER_ACTION_PENDING;
+ }
+ }
+
+ if (mBulkUpdateParams != 0 || mPendingLayoutChanges.size() > 0) {
updateAnimToLayoutLocked();
}
@@ -645,7 +652,8 @@
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges=" + Integer.toHexString(mPendingLayoutChanges));
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY)));
}
}
@@ -691,21 +699,30 @@
}
}
- static class SetAnimationParams {
- final WindowStateAnimator mWinAnimator;
- final Animation mAnimation;
- final int mAnimDw;
- final int mAnimDh;
- public SetAnimationParams(final WindowStateAnimator winAnimator,
- final Animation animation, final int animDw, final int animDh) {
- mWinAnimator = winAnimator;
- mAnimation = animation;
- mAnimDw = animDw;
- mAnimDh = animDh;
+ void clearPendingActions() {
+ synchronized (this) {
+ mPendingActions = 0;
}
}
- synchronized void clearPendingActions() {
- mPendingActions = 0;
+ void setPendingLayoutChanges(final int displayId, final int changes) {
+ mPendingLayoutChanges.put(displayId, mPendingLayoutChanges.get(displayId) | changes);
+ }
+
+ void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
+ // Used to track which displays layout changes have been done.
+ SparseIntArray displays = new SparseIntArray();
+ for (int i = appAnimator.mAllAppWinAnimators.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = appAnimator.mAllAppWinAnimators.get(i);
+ final int displayId = winAnimator.mWin.mDisplayContent.getDisplayId();
+ if (displays.indexOfKey(displayId) < 0) {
+ setPendingLayoutChanges(displayId, changes);
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats(s, mPendingLayoutChanges.get(displayId));
+ }
+ // Keep from processing this display again.
+ displays.put(displayId, changes);
+ }
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 42bc7ce..9a0d280 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -45,7 +45,6 @@
import com.android.server.EventLogTags;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.display.DisplayDeviceInfo;
import com.android.server.display.DisplayManagerService;
import com.android.server.input.InputManagerService;
import com.android.server.power.PowerManagerService;
@@ -87,6 +86,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -100,7 +100,6 @@
import android.util.FloatMath;
import android.util.Log;
import android.util.SparseArray;
-//import android.util.LogPrinter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -110,6 +109,7 @@
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IApplicationToken;
+import android.view.IDisplayContentChangeListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -126,6 +126,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
@@ -163,7 +164,8 @@
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+ implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
+ DisplayManagerService.WindowManagerFuncs {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
@@ -454,8 +456,6 @@
int mSystemDecorLayer = 0;
final Rect mScreenRect = new Rect();
- int mPendingLayoutChanges = 0;
- boolean mLayoutNeeded = true;
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
@@ -719,6 +719,9 @@
*/
boolean mInTouchMode = true;
+ // Temp regions for intermediary calculations.
+ private final Region mTempRegion = new Region();
+
private ViewServer mViewServer;
private ArrayList<WindowChangeListener> mWindowChangeListeners =
new ArrayList<WindowChangeListener>();
@@ -739,116 +742,38 @@
// For example, when this flag is true, there will be no wallpaper service.
final boolean mOnlyCore;
- public static WindowManagerService main(Context context,
- PowerManagerService pm, DisplayManagerService dm,
- boolean haveInputMethods, boolean allowBootMsgs,
- boolean onlyCore) {
- WMThread thr = new WMThread(context, pm, dm, haveInputMethods, allowBootMsgs, onlyCore);
- thr.start();
-
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
+ public static WindowManagerService main(final Context context,
+ final PowerManagerService pm, final DisplayManagerService dm,
+ final Handler uiHandler, final Handler wmHandler,
+ final boolean haveInputMethods, final boolean showBootMsgs,
+ final boolean onlyCore) {
+ final WindowManagerService[] holder = new WindowManagerService[1];
+ wmHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ holder[0] = new WindowManagerService(context, pm, dm,
+ uiHandler, haveInputMethods, showBootMsgs, onlyCore);
}
- return thr.mService;
- }
+ }, 0);
+ return holder[0];
}
- static class WMThread extends Thread {
- WindowManagerService mService;
+ private void initPolicy(Handler uiHandler) {
+ uiHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
- private final Context mContext;
- private final PowerManagerService mPM;
- private final DisplayManagerService mDisplayManager;
- private final boolean mHaveInputMethods;
- private final boolean mAllowBootMessages;
- private final boolean mOnlyCore;
-
- public WMThread(Context context, PowerManagerService pm,
- DisplayManagerService dm,
- boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) {
- super("WindowManager");
- mContext = context;
- mPM = pm;
- mDisplayManager = dm;
- mHaveInputMethods = haveInputMethods;
- mAllowBootMessages = allowBootMsgs;
- mOnlyCore = onlyCore;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
- WindowManagerService s = new WindowManagerService(mContext, mPM, mDisplayManager,
- mHaveInputMethods, mAllowBootMessages, mOnlyCore);
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_DISPLAY);
- android.os.Process.setCanSelfBackground(false);
-
- synchronized (this) {
- mService = s;
- notifyAll();
+ mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
+ mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
}
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper");
- }
-
- Looper.loop();
- }
- }
-
- static class PolicyThread extends Thread {
- private final WindowManagerPolicy mPolicy;
- private final WindowManagerService mService;
- private final Context mContext;
- boolean mRunning = false;
-
- public PolicyThread(WindowManagerPolicy policy,
- WindowManagerService service, Context context) {
- super("WindowManagerPolicy");
- mPolicy = policy;
- mService = service;
- mContext = context;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- WindowManagerPolicyThread.set(this, Looper.myLooper());
-
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_FOREGROUND);
- android.os.Process.setCanSelfBackground(false);
- mPolicy.init(mContext, mService, mService);
- mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
- * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
-
- synchronized (this) {
- mRunning = true;
- notifyAll();
- }
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper");
- }
-
- Looper.loop();
- }
+ }, 0);
}
private WindowManagerService(Context context, PowerManagerService pm,
- DisplayManagerService displayManager,
+ DisplayManagerService displayManager, Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -857,7 +782,7 @@
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
mDisplayManagerService = displayManager;
- mDisplayManager = DisplayManager.getInstance();
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mHeadless = displayManager.isHeadless();
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -895,17 +820,7 @@
mFxSession = new SurfaceSession();
mAnimator = new WindowAnimator(this);
- PolicyThread thr = new PolicyThread(mPolicy, this, context);
- thr.start();
-
- synchronized (thr) {
- while (!thr.mRunning) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
+ initPolicy(uiHandler);
mInputManager.start();
@@ -913,8 +828,11 @@
Watchdog.getInstance().addMonitor(this);
Surface.openTransaction();
- createWatermark();
- Surface.closeTransaction();
+ try {
+ createWatermarkInTransaction();
+ } finally {
+ Surface.closeTransaction();
+ }
}
public InputManagerService getInputManagerService() {
@@ -969,49 +887,65 @@
return -1;
}
+ WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
+ final WindowList windowList = new WindowList();
+ final int count = token.windows.size();
+ for (int i = 0; i < count; i++) {
+ final WindowState win = token.windows.get(i);
+ if (win.mDisplayContent == displayContent) {
+ windowList.add(win);
+ }
+ }
+ return windowList;
+ }
+
private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
final IWindow client = win.mClient;
final WindowToken token = win.mToken;
+ final DisplayContent displayContent = win.mDisplayContent;
final WindowList windows = win.getWindowList();
final int N = windows.size();
final WindowState attached = win.mAttachedWindow;
int i;
+ WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
if (attached == null) {
- int tokenWindowsPos = token.windows.size();
+ int tokenWindowsPos = 0;
+ int windowListPos = tokenWindowList.size();
if (token.appWindowToken != null) {
- int index = tokenWindowsPos-1;
+ int index = windowListPos - 1;
if (index >= 0) {
// If this application has existing windows, we
// simply place the new window on top of them... but
// keep the starting window on top.
if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
// Base windows go behind everything else.
- placeWindowBefore(token.windows.get(0), win);
- tokenWindowsPos = 0;
+ WindowState lowestWindow = tokenWindowList.get(0);
+ placeWindowBefore(lowestWindow, win);
+ tokenWindowsPos = token.windows.indexOf(lowestWindow);
} else {
AppWindowToken atoken = win.mAppToken;
- if (atoken != null &&
- token.windows.get(index) == atoken.startingWindow) {
- placeWindowBefore(token.windows.get(index), win);
- tokenWindowsPos--;
+ WindowState lastWindow = tokenWindowList.get(index);
+ if (atoken != null && lastWindow == atoken.startingWindow) {
+ placeWindowBefore(lastWindow, win);
+ tokenWindowsPos = token.windows.indexOf(lastWindow) - 1;
} else {
- int newIdx = findIdxBasedOnAppTokens(win);
- if(newIdx != -1) {
- //there is a window above this one associated with the same
- //apptoken note that the window could be a floating window
- //that was created later or a window at the top of the list of
- //windows associated with this token.
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "Adding window " + win + " at "
- + (newIdx+1) + " of " + N);
- }
- windows.add(newIdx+1, win);
- mWindowsChanged = true;
+ int newIdx = findIdxBasedOnAppTokens(win);
+ //there is a window above this one associated with the same
+ //apptoken note that the window could be a floating window
+ //that was created later or a window at the top of the list of
+ //windows associated with this token.
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Adding window " + win + " at "
+ + (newIdx+1) + " of " + N);
}
+ windows.add(newIdx+1, win);
+ tokenWindowsPos = token.windows.indexOf(windows.get(newIdx)) + 1;
+ mWindowsChanged = true;
}
}
} else {
+ // No windows from this token on this display
if (localLOGV) Slog.v(
TAG, "Figuring out where to add app window "
+ client.asBinder() + " (token=" + token + ")");
@@ -1027,10 +961,11 @@
}
// We haven't reached the token yet; if this token
- // is not going to the bottom and has windows, we can
+ // is not going to the bottom and has windows on this display, we can
// use it as an anchor for when we do reach the token.
- if (!t.sendingToBottom && t.windows.size() > 0) {
- pos = t.windows.get(0);
+ tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
+ if (!t.sendingToBottom && tokenWindowList.size() > 0) {
+ pos = tokenWindowList.get(0);
}
}
// We now know the index into the apps. If we found
@@ -1040,9 +975,11 @@
// Move behind any windows attached to this one.
WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
if (atoken != null) {
- final int NC = atoken.windows.size();
+ tokenWindowList =
+ getTokenWindowsOnDisplay(atoken, win.mDisplayContent);
+ final int NC = tokenWindowList.size();
if (NC > 0) {
- WindowState bottom = atoken.windows.get(0);
+ WindowState bottom = tokenWindowList.get(0);
if (bottom.mSubLayer < 0) {
pos = bottom;
}
@@ -1051,12 +988,13 @@
placeWindowBefore(pos, win);
} else {
// Continue looking down until we find the first
- // token that has windows.
+ // token that has windows on this display.
while (i >= 0) {
AppWindowToken t = mAnimatingAppTokens.get(i);
- final int NW = t.windows.size();
+ tokenWindowList = getTokenWindowsOnDisplay(t, win.mDisplayContent);
+ final int NW = tokenWindowList.size();
if (NW > 0) {
- pos = t.windows.get(NW-1);
+ pos = tokenWindowList.get(NW-1);
break;
}
i--;
@@ -1098,11 +1036,10 @@
final int myLayer = win.mBaseLayer;
for (i=N-1; i>=0; i--) {
if (windows.get(i).mBaseLayer <= myLayer) {
- i++;
break;
}
}
- if (i < 0) i = 0;
+ i++;
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + win + " at "
+ i + " of " + N);
@@ -1118,12 +1055,12 @@
} else {
// Figure out this window's ordering relative to the window
// it is attached to.
- final int NA = token.windows.size();
+ final int NA = tokenWindowList.size();
final int sublayer = win.mSubLayer;
int largestSublayer = Integer.MIN_VALUE;
WindowState windowWithLargestSublayer = null;
for (i=0; i<NA; i++) {
- WindowState w = token.windows.get(i);
+ WindowState w = tokenWindowList.get(i);
final int wSublayer = w.mSubLayer;
if (wSublayer >= largestSublayer) {
largestSublayer = wSublayer;
@@ -1854,7 +1791,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2097,7 +2034,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2286,7 +2223,11 @@
win.mWinAnimator.mEnterAnimationPending = true;
- mPolicy.getContentInsetHintLw(attrs, outContentInsets);
+ if (displayContent.isDefaultDisplay) {
+ mPolicy.getContentInsetHintLw(attrs, outContentInsets);
+ } else {
+ outContentInsets.setEmpty();
+ }
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -2390,13 +2331,14 @@
if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
win.mExiting = true;
}
+ scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
}
if (win.mExiting || win.mWinAnimator.isAnimating()) {
// The exit animation is running... wait for it!
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
@@ -2513,7 +2455,7 @@
if (!mInLayout) {
assignLayersLocked(windows);
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2579,7 +2521,7 @@
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- mLayoutNeeded = true;
+ w.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -2674,10 +2616,58 @@
window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
(int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
- mLayoutNeeded = true;
+ window.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(window, rectangle,
+ immediate);
+ }
+ }
+ }
+
+ private void scheduleNotifyRectangleOnScreenRequestedIfNeededLocked(WindowState window,
+ Rect rectangle, boolean immediate) {
+ DisplayContent displayContent = window.mDisplayContent;
+ if (displayContent.mDisplayContentChangeListeners != null
+ && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) {
+ mH.obtainMessage(H.NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, displayContent.getDisplayId(),
+ immediate? 1 : 0, new Rect(rectangle)).sendToTarget();
+ }
+ }
+
+ private void handleNotifyRectangleOnScreenRequested(int displayId, Rect rectangle,
+ boolean immediate) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ final int callbackCount = callbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < callbackCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onRectangleOnScreenRequested(displayId,
+ rectangle, immediate);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
@@ -2767,9 +2757,10 @@
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
- boolean focusMayChange = win.mViewVisibility != viewVisibility
+ final boolean isDefaultDisplay = win.isDefaultDisplay();
+ boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
- || (!win.mRelayoutCalled);
+ || (!win.mRelayoutCalled));
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -2851,7 +2842,7 @@
return 0;
}
if (toBeDisplayed) {
- focusMayChange = true;
+ focusMayChange = isDefaultDisplay;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& mInputMethodWindow == null) {
@@ -2888,7 +2879,7 @@
}
if (win.isWinVisibleLw() &&
winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = true;
+ focusMayChange = isDefaultDisplay;
win.mExiting = true;
} else if (win.mWinAnimator.isAnimating()) {
// Currently in a hide animation... turn this into
@@ -2906,6 +2897,7 @@
}
winAnimator.destroySurfaceLocked();
}
+ scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
}
}
@@ -2942,7 +2934,7 @@
}
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
if (assignLayers) {
assignLayersLocked(win.getWindowList());
@@ -3032,7 +3024,7 @@
if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -3051,6 +3043,97 @@
}
}
+ @Override
+ public WindowInfo getWindowInfo(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowInfo()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
+ synchronized (mWindowMap) {
+ WindowState window = mWindowMap.get(token);
+ if (window != null) {
+ return getWindowInfoForWindowStateLocked(window);
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void getVisibleWindowsForDisplay(int displayId, List<WindowInfo> outInfos) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "getWindowInfos()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
+ }
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ WindowList windows = displayContent.getWindowList();
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState window = windows.get(i);
+ if (window.isVisibleLw() ||
+ window.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
+ WindowInfo info = getWindowInfoForWindowStateLocked(window);
+ outInfos.add(info);
+ }
+ }
+ }
+ }
+
+ public void magnifyDisplay(int displayId, float scale, float offsetX, float offsetY) {
+ if (!checkCallingPermission(
+ android.Manifest.permission.MAGNIFY_DISPLAY, "magnifyDisplay()")) {
+ throw new SecurityException("Requires MAGNIFY_DISPLAY permission");
+ }
+ synchronized (mWindowMap) {
+ MagnificationSpec spec = getDisplayMagnificationSpecLocked(displayId);
+ if (spec != null) {
+ final boolean scaleChanged = spec.mScale != scale;
+ final boolean offsetChanged = spec.mOffsetX != offsetX || spec.mOffsetY != offsetY;
+ if (!scaleChanged && !offsetChanged) {
+ return;
+ }
+ spec.initialize(scale, offsetX, offsetY);
+ // If the offset has changed we need to re-add the input windows
+ // since the offsets have to be propagated to the input system.
+ if (offsetChanged) {
+ // TODO(multidisplay): Input only occurs on the default display.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mInputMonitor.updateInputWindowsLw(true);
+ }
+ }
+ scheduleAnimationLocked();
+ }
+ }
+ }
+
+ MagnificationSpec getDisplayMagnificationSpecLocked(int displayId) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent != null) {
+ if (displayContent.mMagnificationSpec == null) {
+ displayContent.mMagnificationSpec = new MagnificationSpec();
+ }
+ return displayContent.mMagnificationSpec;
+ }
+ return null;
+ }
+
+ private WindowInfo getWindowInfoForWindowStateLocked(WindowState window) {
+ WindowInfo info = WindowInfo.obtain();
+ info.token = window.mToken.token;
+ info.frame.set(window.mFrame);
+ info.type = window.mAttrs.type;
+ info.displayId = window.getDisplayId();
+ info.compatibilityScale = window.mGlobalScale;
+ info.visible = window.isVisibleLw()
+ || info.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
+ window.getTouchableRegion(mTempRegion);
+ mTempRegion.getBounds(info.touchableRegion);
+ return info;
+ }
+
private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
@@ -3545,12 +3628,14 @@
if (win.isVisibleNow()) {
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_EXIT);
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
if (changed) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
false /*updateInputWindows*/);
@@ -3806,7 +3891,7 @@
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(false);
config = new Configuration(mTempConfiguration);
}
@@ -3892,6 +3977,7 @@
}
}
+ @Override
public int getAppOrientation(IApplicationToken token) {
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3903,6 +3989,7 @@
}
}
+ @Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setFocusedApp()")) {
@@ -3940,6 +4027,7 @@
}
}
+ @Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
@@ -4169,7 +4257,7 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
true /*updateInputWindows*/);
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
return;
@@ -4315,6 +4403,10 @@
if (applyAnimationLocked(wtoken, lp, transit, visible)) {
delayed = runningAppAnimation = true;
}
+ WindowState window = wtoken.findMainWindow();
+ if (window != null) {
+ scheduleNotifyWindowTranstionIfNeededLocked(window, transit);
+ }
changed = true;
}
@@ -4332,15 +4424,21 @@
if (!runningAppAnimation) {
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_ENTER, true);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_ENTER);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
win.mWinAnimator.applyAnimationLocked(
WindowManagerPolicy.TRANSIT_EXIT, false);
+ scheduleNotifyWindowTranstionIfNeededLocked(win,
+ WindowManagerPolicy.TRANSIT_EXIT);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
@@ -4362,7 +4460,6 @@
+ wtoken.hiddenRequested);
if (changed) {
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4490,6 +4587,7 @@
mInnerFields.mOrientationChangeComplete = false;
}
unfrozeWindows = true;
+ w.mDisplayContent.layoutNeeded = true;
}
}
if (force || unfrozeWindows) {
@@ -4499,7 +4597,6 @@
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
stopFreezingDisplayLocked();
@@ -4792,7 +4889,7 @@
for (int i=0; i<NW; i++) {
final WindowState win = token.windows.get(i);
if (win.mDisplayContent == displayContent) {
- index = reAddWindowLocked(index, token.windows.get(i));
+ index = reAddWindowLocked(index, win);
}
}
return index;
@@ -4844,13 +4941,15 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, index);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
}
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4890,7 +4989,10 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, tokenPos);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
@@ -4903,7 +5005,6 @@
// Note that the above updateFocusedWindowLocked conditional used to sit here.
- mLayoutNeeded = true;
if (!mInLayout) {
performLayoutAndPlaceSurfacesLocked();
}
@@ -4932,7 +5033,11 @@
for (i=0; i<N; i++) {
WindowToken token = mTokenMap.get(tokens.get(i));
if (token != null) {
- pos = reAddAppWindowsLocked(displayContent, pos, token);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, token);
+ if (newPos != pos) {
+ displayContent.layoutNeeded = true;
+ }
+ pos = newPos;
}
}
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4945,7 +5050,6 @@
// Note that the above updateFocusedWindowLocked used to sit here.
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -5739,7 +5843,7 @@
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -5817,42 +5921,46 @@
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(inTransaction);
- mInputManager.setDisplayOrientation(0, rotation, Surface.ROTATION_0);
+ mInputManager.setDisplayOrientation(0, rotation);
// We need to update our screen size information to match the new
// rotation. Note that this is redundant with the later call to
// sendNewConfiguration() that must be called after this function
// returns... however we need to do the screen size part of that
- // before then so we have the correct size to use when initializiation
+ // before then so we have the correct size to use when initializing
// the rotation animation for the new rotation.
computeScreenConfigurationLocked(null);
- if (!inTransaction) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION setRotationUnchecked");
- Surface.openTransaction();
- }
final DisplayContent displayContent = getDefaultDisplayContent();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ if (!inTransaction) {
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");
+ }
+ Surface.openTransaction();
+ }
try {
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null
&& mAnimator.mScreenRotationAnimation.hasScreenshot()) {
- if (mAnimator.mScreenRotationAnimation.setRotation(rotation, mFxSession,
+ if (mAnimator.mScreenRotationAnimation.setRotationInTransaction(
+ rotation, mFxSession,
MAX_ANIMATION_DURATION, mTransitionAnimationScale,
displayInfo.logicalWidth, displayInfo.logicalHeight)) {
updateLayoutToAnimationLocked();
}
}
- Surface.setOrientation(0, rotation);
+
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
Surface.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setRotationUnchecked");
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");
+ }
}
}
@@ -5867,12 +5975,16 @@
mInnerFields.mOrientationChangeComplete = false;
}
}
+
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
try {
mRotationWatchers.get(i).onRotationChanged(rotation);
} catch (RemoteException e) {
}
}
+
+ scheduleNotifyRotationChangedIfNeededLocked(displayContent, rotation);
+
return true;
}
@@ -6253,6 +6365,110 @@
return success;
}
+ public void addDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "addDisplayContentChangeListener()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
+ }
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent.mDisplayContentChangeListeners == null) {
+ displayContent.mDisplayContentChangeListeners =
+ new RemoteCallbackList<IDisplayContentChangeListener>();
+ displayContent.mDisplayContentChangeListeners.register(listener);
+ }
+ }
+ }
+
+ public void removeDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) {
+ if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
+ "removeDisplayContentChangeListener()")) {
+ throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission");
+ }
+ synchronized(mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent.mDisplayContentChangeListeners != null) {
+ displayContent.mDisplayContentChangeListeners.unregister(listener);
+ if (displayContent.mDisplayContentChangeListeners
+ .getRegisteredCallbackCount() == 0) {
+ displayContent.mDisplayContentChangeListeners = null;
+ }
+ }
+ }
+ }
+
+ void scheduleNotifyWindowTranstionIfNeededLocked(WindowState window, int transition) {
+ DisplayContent displayContent = window.mDisplayContent;
+ if (displayContent.mDisplayContentChangeListeners != null) {
+ WindowInfo info = getWindowInfoForWindowStateLocked(window);
+ mH.obtainMessage(H.NOTIFY_WINDOW_TRANSITION, transition, 0, info).sendToTarget();
+ }
+ }
+
+ private void handleNotifyWindowTranstion(int transition, WindowInfo info) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(info.displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ final int callbackCount = callbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < callbackCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onWindowTransition(info.displayId,
+ transition, info);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
+ private void scheduleNotifyRotationChangedIfNeededLocked(DisplayContent displayContent,
+ int rotation) {
+ if (displayContent.mDisplayContentChangeListeners != null
+ && displayContent.mDisplayContentChangeListeners.getRegisteredCallbackCount() > 0) {
+ mH.obtainMessage(H.NOTIFY_ROTATION_CHANGED, displayContent.getDisplayId(),
+ rotation).sendToTarget();
+ }
+ }
+
+ private void handleNotifyRotationChanged(int displayId, int rotation) {
+ RemoteCallbackList<IDisplayContentChangeListener> callbacks = null;
+ synchronized (mWindowMap) {
+ DisplayContent displayContent = getDisplayContent(displayId);
+ if (displayContent == null) {
+ return;
+ }
+ callbacks = displayContent.mDisplayContentChangeListeners;
+ if (callbacks == null) {
+ return;
+ }
+ }
+ try {
+ final int watcherCount = callbacks.beginBroadcast();
+ for (int i = 0; i < watcherCount; i++) {
+ try {
+ callbacks.getBroadcastItem(i).onRotationChanged(rotation);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ callbacks.finishBroadcast();
+ }
+ }
+
public void addWindowChangeListener(WindowChangeListener listener) {
synchronized(mWindowMap) {
mWindowChangeListeners.add(listener);
@@ -6347,6 +6563,7 @@
}
private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation);
if (width < displayInfo.smallestNominalAppWidth) {
displayInfo.smallestNominalAppWidth = width;
@@ -6365,6 +6582,7 @@
private int reduceConfigLayout(int curLayout, int rotation, float density,
int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
// Get the app screen size at this rotation.
int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
@@ -6444,6 +6662,8 @@
private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
int dw, int dh, float density, Configuration outConfig) {
+ // TODO: Multidisplay: for now only use with default display.
+
// We need to determine the smallest width that will occur under normal
// operation. To this, start with the base screen size and compute the
// width under the different possible rotations. We need to un-rotate
@@ -6471,11 +6691,13 @@
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
- outConfig.screenLayout = sl;
+ outConfig.screenLayout =
+ sl|(outConfig.screenLayout&Configuration.SCREENLAYOUT_LAYOUTDIR_MASK);
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
@@ -6487,9 +6709,10 @@
}
private int computeCompatSmallestWidth(boolean rotated, DisplayMetrics dm, int dw, int dh) {
+ // TODO: Multidisplay: for now only use with default display.
mTmpDisplayMetrics.setTo(dm);
- dm = mTmpDisplayMetrics;
- int unrotDw, unrotDh;
+ final DisplayMetrics tmpDm = mTmpDisplayMetrics;
+ final int unrotDw, unrotDh;
if (rotated) {
unrotDw = dh;
unrotDh = dw;
@@ -6497,10 +6720,10 @@
unrotDw = dw;
unrotDh = dh;
}
- int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, dm, unrotDw, unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, dm, unrotDh, unrotDw);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, dm, unrotDw, unrotDh);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, dm, unrotDh, unrotDw);
+ int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw);
return sw;
}
@@ -6561,7 +6784,8 @@
displayInfo.appHeight = appHeight;
displayInfo.getLogicalMetrics(mRealDisplayMetrics, null);
displayInfo.getAppMetrics(mDisplayMetrics, null);
- mDisplayManagerService.setDisplayInfo(displayContent.getDisplayId(), displayInfo);
+ mDisplayManagerService.setDisplayInfoOverrideFromWindowManager(
+ displayContent.getDisplayId(), displayInfo);
mAnimator.setDisplayDimensions(dw, dh, appWidth, appHeight);
}
@@ -6711,9 +6935,9 @@
synchronized (mWindowMap) {
try {
if (mDragState == null) {
- Surface surface = new Surface(session, callerPid, "drag surface",
- mDefaultDisplay.getLayerStack(),
+ Surface surface = new Surface(session, "drag surface",
width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ surface.setLayerStack(mDefaultDisplay.getLayerStack());
if (SHOW_TRANSACTIONS) Slog.i(TAG, " DRAG "
+ surface + ": CREATE");
outSurface.copyFrom(surface);
@@ -6808,21 +7032,6 @@
}
}
- public boolean getWindowFrame(IBinder token, Rect outBounds) {
- if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
- "getWindowFrame()")) {
- throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
- }
- synchronized (mWindowMap) {
- WindowState windowState = mWindowMap.get(token);
- if (windowState != null) {
- outBounds.set(windowState.getFrameLw());
- return true;
- }
- }
- return false;
- }
-
private WindowState getFocusedWindow() {
synchronized (mWindowMap) {
return getFocusedWindowLocked();
@@ -6887,15 +7096,11 @@
mAnimator.setDisplayDimensions(displayInfo.logicalWidth, displayInfo.logicalHeight,
displayInfo.appWidth, displayInfo.appHeight);
- DisplayDeviceInfo info = new DisplayDeviceInfo();
- mDisplayManagerService.getDefaultExternalDisplayDeviceInfo(info);
-
final DisplayContent displayContent = getDefaultDisplayContent();
mInputManager.setDisplaySize(displayContent.getDisplayId(),
- displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight,
- info.width, info.height);
+ displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight);
mInputManager.setDisplayOrientation(displayContent.getDisplayId(),
- mDefaultDisplay.getRotation(), Surface.ROTATION_0);
+ mDefaultDisplay.getRotation());
mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mInitialDisplayWidth,
displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
}
@@ -6913,7 +7118,10 @@
synchronized(displayContent.mDisplaySizeLock) {
// Bootstrap the default logical display from the display manager.
displayInfo = displayContent.getDisplayInfo();
- mDisplayManagerService.getDisplayInfo(displayId, displayInfo);
+ DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId);
+ if (newDisplayInfo != null) {
+ displayInfo.copyFrom(newDisplayInfo);
+ }
displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
@@ -6971,6 +7179,9 @@
public static final int UPDATE_ANIM_PARAMETERS = 25;
public static final int SHOW_STRICT_MODE_VIOLATION = 26;
public static final int DO_ANIMATION_CALLBACK = 27;
+ public static final int NOTIFY_ROTATION_CHANGED = 28;
+ public static final int NOTIFY_WINDOW_TRANSITION = 29;
+ public static final int NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 30;
public static final int ANIMATOR_WHAT_OFFSET = 100000;
public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
@@ -7409,6 +7620,25 @@
}
break;
}
+ case NOTIFY_ROTATION_CHANGED: {
+ final int displayId = msg.arg1;
+ final int rotation = msg.arg2;
+ handleNotifyRotationChanged(displayId, rotation);
+ break;
+ }
+ case NOTIFY_WINDOW_TRANSITION: {
+ final int transition = msg.arg1;
+ WindowInfo info = (WindowInfo) msg.obj;
+ handleNotifyWindowTranstion(transition, info);
+ break;
+ }
+ case NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+ final int displayId = msg.arg1;
+ final boolean immediate = (msg.arg2 == 1);
+ Rect rectangle = (Rect) msg.obj;
+ handleNotifyRectangleOnScreenRequested(displayId, rectangle, immediate);
+ break;
+ }
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -7654,10 +7884,11 @@
}
private void reconfigureDisplayLocked(DisplayContent displayContent) {
+ // TODO: Multidisplay: for now only use with default display.
mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth,
displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
- mLayoutNeeded = true;
+ displayContent.layoutNeeded = true;
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
@@ -7912,31 +8143,11 @@
}
try {
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while (iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- performLayoutAndPlaceSurfacesLockedInner(displayContent, recoveringMemory);
-
- final int N = mPendingRemove.size();
- if (N > 0) {
- if (mPendingRemoveTmp.length < N) {
- mPendingRemoveTmp = new WindowState[N+10];
- }
- mPendingRemove.toArray(mPendingRemoveTmp);
- mPendingRemove.clear();
- for (int i=0; i<N; i++) {
- WindowState w = mPendingRemoveTmp[i];
- removeWindowInnerLocked(w.mSession, w);
- }
-
- assignLayersLocked(displayContent.getWindowList());
- mLayoutNeeded = true;
- }
- }
+ performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
mInLayout = false;
- if (mLayoutNeeded) {
+ if (needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
} else {
@@ -7961,11 +8172,12 @@
private final void performLayoutLockedInner(final DisplayContent displayContent,
boolean initial, boolean updateInputWindows) {
- if (!mLayoutNeeded) {
+ if (!displayContent.layoutNeeded) {
return;
}
+ displayContent.layoutNeeded = false;
WindowList windows = displayContent.getWindowList();
- mLayoutNeeded = false;
+ boolean isDefaultDisplay = displayContent.isDefaultDisplay;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
@@ -7982,14 +8194,17 @@
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed="
- + mLayoutNeeded + " dw=" + dw + " dh=" + dh);
+ + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
}
WindowStateAnimator universeBackground = null;
- mPolicy.beginLayoutLw(dw, dh, mRotation);
- mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect);
- mScreenRect.set(0, 0, dw, dh);
+ mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation);
+ if (isDefaultDisplay) {
+ // Not needed on non-default displays.
+ mSystemDecorLayer = mPolicy.getSystemDecorRectLw(mSystemDecorRect);
+ mScreenRect.set(0, 0, dw, dh);
+ }
int seq = mLayoutSeq+1;
if (seq < 0) seq = 0;
@@ -8024,7 +8239,7 @@
+ (atoken != null && atoken.hiddenRequested)
+ " mAttachedHidden=" + win.mAttachedHidden);
}
-
+
// If this view is GONE, then skip it -- keep the current
// frame, and let the caller know so they can ignore it
// if they want. (We do the normal layout for INVISIBLE
@@ -8127,8 +8342,7 @@
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @param windows TODO(cmautner):
- *
+ * @param windows List of windows on default display.
* @return bitmap indicating if another pass through layout must be made.
*/
public int handleAppTransitionReadyLocked(WindowList windows) {
@@ -8342,10 +8556,11 @@
Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
mNextAppTransitionThumbnail.getHeight());
try {
- Surface surface = new Surface(mFxSession, Process.myPid(),
- "thumbnail anim", mDefaultDisplay.getLayerStack(),
+ Surface surface = new Surface(mFxSession,
+ "thumbnail anim",
dirty.width(), dirty.height(),
PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ surface.setLayerStack(mDefaultDisplay.getLayerStack());
topOpeningApp.mAppAnimator.thumbnail = surface;
if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
+ surface + ": CREATE");
@@ -8381,9 +8596,9 @@
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
- changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
| WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) {
@@ -8605,24 +8820,13 @@
}
// "Something has changed! Let's make it correct now."
- private final void performLayoutAndPlaceSurfacesLockedInner(
- final DisplayContent displayContent, boolean recoveringMemory) {
+ private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
+ Debug.getCallers(3));
}
- if (mDefaultDisplay == null) {
- Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
- return;
- }
- final WindowList windows = displayContent.getWindowList();
final long currentTime = SystemClock.uptimeMillis();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
- final int innerDw = displayInfo.appWidth;
- final int innerDh = displayInfo.appHeight;
int i;
@@ -8647,198 +8851,236 @@
mInnerFields.mButtonBrightness = -1;
mTransactionSequence++;
+ final DisplayContent defaultDisplay = getDefaultDisplayContent();
+ final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
+ final int defaultDw = defaultInfo.logicalWidth;
+ final int defaultDh = defaultInfo.logicalHeight;
+ final int defaultInnerDw = defaultInfo.appWidth;
+ final int defaultInnerDh = defaultInfo.appHeight;
+
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-
Surface.openTransaction();
-
- if (mWatermark != null) {
- mWatermark.positionSurface(dw, dh);
- }
- if (mStrictModeFlash != null) {
- mStrictModeFlash.positionSurface(dw, dh);
- }
-
try {
- int repeats = 0;
- do {
- repeats++;
- if (repeats > 6) {
- Slog.w(TAG, "Animation repeat aborted after too many iterations");
- mLayoutNeeded = false;
- break;
- }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(defaultDw, defaultDh);
+ }
+ if (mStrictModeFlash != null) {
+ mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ }
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
- mPendingLayoutChanges);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- if ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- assignLayersLocked(windows);
- mLayoutNeeded = true;
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked(true)) {
- mLayoutNeeded = true;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- mLayoutNeeded = true;
- }
-
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < 4) {
- performLayoutLockedInner(displayContent, repeats == 1, false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
-
- // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
- // it is animating.
- mPendingLayoutChanges = 0;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
- mPendingLayoutChanges);
- mPolicy.beginAnimationLw(dw, dh);
- for (i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
- if (w.mHasSurface) {
- mPolicy.animatingWindowLw(w, w.mAttrs);
- }
- }
- mPendingLayoutChanges |= mPolicy.finishAnimationLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
- mPendingLayoutChanges);
- } while (mPendingLayoutChanges != 0);
-
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
-
- mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
- mInnerFields.mSyswin = false;
+ // Give the display manager a chance to adjust properties
+ // like display rotation if it needs to.
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
boolean focusDisplayed = false;
boolean updateAllDrawn = false;
- final int N = windows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = windows.get(i);
- final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ WindowList windows = displayContent.getWindowList();
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int displayId = displayContent.getDisplayId();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- // Update effect.
- w.mObscured = mInnerFields.mObscured;
- if (!mInnerFields.mObscured) {
- handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
- }
-
- if (obscuredChanged && (mWallpaperTarget == w) && w.isVisibleLw()) {
- // This is the wallpaper target and its obscured state
- // changed... make sure the current wallaper's visibility
- // has been updated accordingly.
- updateWallpaperVisibilityLocked();
- }
-
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
- // If the window has moved due to its containing
- // content frame changing, then we'd like to animate
- // it.
- if (w.mHasSurface && w.shouldAnimateMove()) {
- // Frame has moved, containing content frame
- // has also moved, and we're not currently animating...
- // let's do something.
- Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
- winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
- try {
- w.mClient.moved(w.mFrame.left, w.mFrame.top);
- } catch (RemoteException e) {
+ int repeats = 0;
+ do {
+ repeats++;
+ if (repeats > 6) {
+ Slog.w(TAG, "Animation repeat aborted after too many iterations");
+ displayContent.layoutNeeded = false;
+ break;
}
- }
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ displayContent.pendingLayoutChanges);
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- if (winAnimator.commitFinishDrawingLocked(currentTime)) {
- if ((w.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
- mInnerFields.mWallpaperMayChange = true;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",
- mPendingLayoutChanges);
+ if (isDefaultDisplay && ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0)
+ && ((adjustWallpaperWindowsLocked()
+ & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0)) {
+ assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
+ }
+
+ if (isDefaultDisplay && (displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+ if (updateOrientationFromAppTokensLocked(true)) {
+ displayContent.layoutNeeded = true;
+ mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+ }
+ }
+
+ if ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ displayContent.layoutNeeded = true;
+ }
+
+ // FIRST LOOP: Perform a layout, if needed.
+ if (repeats < 4) {
+ performLayoutLockedInner(displayContent, repeats == 1,
+ false /*updateInputWindows*/);
+ } else {
+ Slog.w(TAG, "Layout repeat skipped after too many iterations");
+ }
+
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ displayContent.pendingLayoutChanges = 0;
+
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number "
+ + mLayoutRepeatCount, displayContent.pendingLayoutChanges);
+
+ if (isDefaultDisplay) {
+ mPolicy.beginPostLayoutPolicyLw(dw, dh);
+ for (i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mHasSurface) {
+ mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs);
}
}
+ displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats(
+ "after finishPostLayoutPolicyLw", displayContent.pendingLayoutChanges);
+ }
+ } while (displayContent.pendingLayoutChanges != 0);
+
+ mInnerFields.mObscured = false;
+ mInnerFields.mDimming = false;
+ mInnerFields.mSyswin = false;
+
+ // Only used if default window
+ final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
+
+ final int N = windows.size();
+ for (i=N-1; i>=0; i--) {
+ WindowState w = windows.get(i);
+
+ final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+
+ // Update effect.
+ w.mObscured = mInnerFields.mObscured;
+ if (!mInnerFields.mObscured) {
+ handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
}
- winAnimator.setSurfaceBoundaries(recoveringMemory);
-
- final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
- + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
+ && w.isVisibleLw()) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
}
- if (atoken != null
- && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mTransactionSequence) {
- atoken.lastTransactionSequence = mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.startingDisplayed = false;
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ // If the window has moved due to its containing
+ // content frame changing, then we'd like to animate
+ // it.
+ if (w.mHasSurface && w.shouldAnimateMove()) {
+ // Frame has moved, containing content frame
+ // has also moved, and we're not currently animating...
+ // let's do something.
+ Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ winAnimator.setAnimation(a);
+ winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+ winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+ try {
+ w.mClient.moved(w.mFrame.left, w.mFrame.top);
+ } catch (RemoteException e) {
}
- if ((w.isOnScreen() || winAnimator.mAttrType
- == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
- && !w.mExiting && !w.mDestroying) {
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimating=" + winAnimator.isAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.mDrawState
- + " ah=" + w.mAttachedHidden
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
+ }
+
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+
+ // Moved from updateWindowsAndWallpaperLocked().
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ final boolean committed =
+ winAnimator.commitFinishDrawingLocked(currentTime);
+ if (isDefaultDisplay && committed) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ mInnerFields.mWallpaperMayChange = true;
+ displayContent.pendingLayoutChanges |=
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats(
+ "wallpaper and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
}
}
- if (w != atoken.startingWindow) {
- if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
- "tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
+ }
+
+ winAnimator.setSurfaceBoundaries(recoveringMemory);
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
+ + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ }
+ if (atoken != null
+ && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken.lastTransactionSequence != mTransactionSequence) {
+ atoken.lastTransactionSequence = mTransactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreen() || winAnimator.mAttrType
+ == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimating=" + winAnimator.isAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.mDrawState
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + winAnimator.mAnimating);
}
}
- } else if (w.isDrawnLw()) {
- atoken.startingDisplayed = true;
+ if (w != atoken.startingWindow) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDrawnLw()) {
+ atoken.numDrawnWindows++;
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ "tokenMayBeDrawn: " + atoken
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ updateAllDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ atoken.startingDisplayed = true;
+ }
}
}
}
- }
- if (someoneLosingFocus && w == mCurrentFocus && w.isDisplayedLw()) {
- focusDisplayed = true;
- }
+ if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
+ && w.isDisplayedLw()) {
+ focusDisplayed = true;
+ }
- updateResizingWindows(w);
+ updateResizingWindows(w);
+ }
}
if (updateAllDrawn) {
@@ -8858,13 +9100,15 @@
Surface.closeTransaction();
}
+ final WindowList defaultWindows = defaultDisplay.getWindowList();
+
// If we are ready to perform an app transition, check through
// all of the app tokens to be shown and see if they are ready
// to go.
if (mAppTransitionReady) {
- mPendingLayoutChanges |= handleAppTransitionReadyLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8876,22 +9120,22 @@
// reflects the correct Z-order, but the window list may now
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
- mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
+ defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
- if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
- !mAppTransitionReady) {
+ if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
+ && !mAppTransitionReady) {
// At this point, there was a window with a wallpaper that
// was force hiding other windows behind it, but now it
// is going away. This may be simple -- just animate
// away the wallpaper and its window -- or it may be
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
- mPendingLayoutChanges |= animateAwayWallpaperLocked();
+ defaultDisplay.pendingLayoutChanges |= animateAwayWallpaperLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mWallpaperForceHidingChanged = false;
@@ -8904,26 +9148,27 @@
if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- assignLayersLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ assignLayersLocked(defaultWindows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
}
if (mFocusMayChange) {
mFocusMayChange = false;
if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
mInnerFields.mAdjResult = 0;
}
}
- if (mLayoutNeeded) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
+ if (needsLayout()) {
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
+ defaultDisplay.pendingLayoutChanges);
}
if (!mResizingWindows.isEmpty()) {
@@ -9031,11 +9276,16 @@
mRelayoutWhileAnimating.clear();
}
- if (wallpaperDestroyed) {
- mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
+ if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) {
+ getDefaultDisplayContent().layoutNeeded = true;
}
- if (mPendingLayoutChanges != 0) {
- mLayoutNeeded = true;
+
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ DisplayContent displayContent = iterator.next();
+ if (displayContent.pendingLayoutChanges != 0) {
+ displayContent.layoutNeeded = true;
+ }
}
// Finally update all input windows now that the window changes have stabilized.
@@ -9072,11 +9322,33 @@
}
}
- if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
- !mInnerFields.mUpdateRotation) {
+ if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded
+ && !mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ final int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mPendingRemove.toArray(mPendingRemoveTmp);
+ mPendingRemove.clear();
+ DisplayContentList displayList = new DisplayContentList();
+ for (i = 0; i < N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ if (!displayList.contains(w.mDisplayContent)) {
+ displayList.add(w.mDisplayContent);
+ }
+ }
+
+ for (DisplayContent displayContent : displayList) {
+ assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
@@ -9084,9 +9356,8 @@
updateLayoutToAnimationLocked();
if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
- + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
- + " animating=" + mAnimator.mAnimating);
+ Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating="
+ + mAnimator.mAnimating);
}
}
@@ -9159,6 +9430,12 @@
}
}
+ public void requestTraversal() {
+ synchronized (mWindowMap) {
+ requestTraversalLocked();
+ }
+ }
+
void requestTraversalLocked() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
@@ -9236,6 +9513,16 @@
setAnimDimParams(null);
}
+ private boolean needsLayout() {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().layoutNeeded) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean copyAnimToLayoutParamsLocked() {
boolean doRequest = false;
final WindowAnimator.AnimatorToLayoutParams animToLayout = mAnimator.mAnimToLayout;
@@ -9268,10 +9555,15 @@
mTurnOnScreen = true;
}
- mPendingLayoutChanges |= animToLayout.mPendingLayoutChanges;
- if (mPendingLayoutChanges != 0) {
+ SparseIntArray pendingLayouts = animToLayout.mPendingLayoutChanges;
+ final int count = pendingLayouts.size();
+ if (count > 0) {
doRequest = true;
}
+ for (int i = 0; i < count; ++i) {
+ final DisplayContent displayContent = getDisplayContent(pendingLayouts.keyAt(i));
+ displayContent.pendingLayoutChanges |= pendingLayouts.valueAt(i);
+ }
mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper;
}
@@ -9407,7 +9699,7 @@
if (moveInputMethodWindowsIfNeededLocked(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
@@ -9419,9 +9711,9 @@
}
}
- if ((focusChanged&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
}
@@ -9654,7 +9946,7 @@
return val;
}
- void createWatermark() {
+ void createWatermarkInTransaction() {
if (mWatermark != null) {
return;
}
@@ -10083,8 +10375,18 @@
}
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
- pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded);
- pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
+ if (needsLayout()) {
+ pw.print(" layoutNeeded on displays=");
+ DisplayContentsIterator dcIterator = new DisplayContentsIterator();
+ while (dcIterator.hasNext()) {
+ final DisplayContent displayContent = dcIterator.next();
+ if (displayContent.layoutNeeded) {
+ pw.print(displayContent.getDisplayId());
+ }
+ }
+ pw.println();
+ }
+ pw.print(" mTransactionSequence="); pw.println(mTransactionSequence);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
@@ -10363,7 +10665,7 @@
public DisplayContent getDisplayContent(final int displayId) {
DisplayContent displayContent = mDisplayContents.get(displayId);
if (displayContent == null) {
- displayContent = new DisplayContent(mDisplayManager.getRealDisplay(displayId));
+ displayContent = new DisplayContent(mDisplayManager.getDisplay(displayId));
mDisplayContents.put(displayId, displayContent);
}
return displayContent;
@@ -10464,4 +10766,7 @@
return getDefaultDisplayContent().getDisplayInfo();
}
+ public WindowList getWindowList(final Display display) {
+ return getDisplayContent(display.getDisplayId()).getWindowList();
+ }
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a52e1d7..478475d 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -514,6 +514,21 @@
}
}
+ MagnificationSpec getWindowMagnificationSpecLocked() {
+ MagnificationSpec spec = mDisplayContent.mMagnificationSpec;
+ if (spec != null && !spec.isNop()) {
+ if (mAttachedWindow != null) {
+ if (!mPolicy.canMagnifyWindow(mAttachedWindow.mAttrs)) {
+ return null;
+ }
+ }
+ if (!mPolicy.canMagnifyWindow(mAttrs)) {
+ return null;
+ }
+ }
+ return spec;
+ }
+
@Override
public Rect getFrameLw() {
return mFrame;
@@ -587,10 +602,12 @@
}
}
+ @Override
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
+ @Override
public int getSurfaceLayer() {
return mLayer;
}
@@ -598,7 +615,11 @@
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
-
+
+ public int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
public long getInputDispatchingTimeoutNanos() {
return mAppToken != null
? mAppToken.inputDispatchingTimeoutNanos
@@ -984,6 +1005,11 @@
return mClient.asBinder().isBinderAlive();
}
+ @Override
+ public boolean isDefaultDisplay() {
+ return mDisplayContent.isDefaultDisplay;
+ }
+
boolean isOtherUsersAppWindow() {
final int type = mAttrs.type;
if ((UserHandle.getUserId(mOwnerUid) != mService.mCurrentUserId)
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 982f60d..8912c73 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -3,7 +3,6 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
@@ -335,7 +334,7 @@
+ mWin.mPolicyVisibilityAfterAnim);
}
mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
- mService.mLayoutNeeded = true;
+ mWin.mDisplayContent.layoutNeeded = true;
if (!mWin.mPolicyVisibility) {
if (mService.mCurrentFocus == mWin) {
mService.mFocusMayChange = true;
@@ -359,9 +358,10 @@
}
finishExit();
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = mWin.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges.get(displayId));
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -481,9 +481,9 @@
private String mName;
public SurfaceTrace(SurfaceSession s,
- int pid, String name, int layerStack, int w, int h, int format, int flags)
+ String name, int w, int h, int format, int flags)
throws OutOfResourcesException {
- super(s, pid, name, layerStack, w, h, format, flags);
+ super(s, name, w, h, format, flags);
mName = name != null ? name : "Not named";
mSize.set(w, h);
Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
@@ -608,7 +608,7 @@
mService.makeWindowFreezingScreenIfNeededLocked(mWin);
- int flags = 0;
+ int flags = Surface.HIDDEN;
final WindowManager.LayoutParams attrs = mWin.mAttrs;
if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
@@ -652,14 +652,14 @@
}
if (DEBUG_SURFACE_TRACE) {
mSurface = new SurfaceTrace(
- mSession.mSurfaceSession, mSession.mPid,
+ mSession.mSurfaceSession,
attrs.getTitle().toString(),
- mLayerStack, w, h, format, flags);
+ w, h, format, flags);
} else {
mSurface = new Surface(
- mSession.mSurfaceSession, mSession.mPid,
+ mSession.mSurfaceSession,
attrs.getTitle().toString(),
- mLayerStack, w, h, format, flags);
+ w, h, format, flags);
}
mWin.mHasSurface = true;
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
@@ -701,10 +701,10 @@
mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
mSurface.setPosition(mSurfaceX, mSurfaceY);
mSurfaceLayer = mAnimLayer;
+ mSurface.setLayerStack(mLayerStack);
mSurface.setLayer(mAnimLayer);
mSurface.setAlpha(0);
mSurfaceShown = false;
- mSurface.hide();
} catch (RuntimeException e) {
Slog.w(TAG, "Error creating surface in " + w, e);
mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
@@ -886,6 +886,11 @@
tmpMatrix.postConcat(
mService.mAnimator.mScreenRotationAnimation.getEnterTransformation().getMatrix());
}
+ MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked();
+ if (spec != null && !spec.isNop()) {
+ tmpMatrix.postScale(spec.mScale, spec.mScale);
+ tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY);
+ }
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
@@ -953,16 +958,30 @@
if (WindowManagerService.localLOGV) Slog.v(
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- if (mAnimator.mUniverseBackground != null &&
- mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
- && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer) {
+
+ final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null
+ && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+ && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
+ MagnificationSpec spec = mWin.getWindowMagnificationSpecLocked();
+ if (applyUniverseTransformation || spec != null) {
final Rect frame = mWin.mFrame;
final float tmpFloats[] = mService.mTmpFloats;
final Matrix tmpMatrix = mWin.mTmpMatrix;
+
tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
- tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
+
+ if (applyUniverseTransformation) {
+ tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
+ }
+
+ if (spec != null && !spec.isNop()) {
+ tmpMatrix.postScale(spec.mScale, spec.mScale);
+ tmpMatrix.postTranslate(spec.mOffsetX, spec.mOffsetY);
+ }
+
tmpMatrix.getValues(tmpFloats);
+
mHaveMatrix = true;
mDsDx = tmpFloats[Matrix.MSCALE_X];
mDtDx = tmpFloats[Matrix.MSKEW_Y];
@@ -972,8 +991,12 @@
float y = tmpFloats[Matrix.MTRANS_Y];
int w = frame.width();
int h = frame.height();
- mWin.mShownFrame.set(x, y, x+w, y+h);
- mShownAlpha = mAlpha * mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
+ mWin.mShownFrame.set(x, y, x + w, y + h);
+
+ mShownAlpha = mAlpha;
+ if (applyUniverseTransformation) {
+ mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
+ }
} else {
mWin.mShownFrame.set(mWin.mFrame);
if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
@@ -1015,12 +1038,16 @@
void updateSurfaceWindowCrop(final boolean recoveringMemory) {
final WindowState w = mWin;
+ DisplayInfo displayInfo = w.mDisplayContent.getDisplayInfo();
// Need to recompute a new system decor rect each time.
if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
// Currently can't do this cropping for scaled windows. We'll
// just keep the crop rect the same as the source surface.
w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
+ } else if (!w.isDefaultDisplay()) {
+ // On a different display is easy, just use the entire display.
+ w.mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
} else if (w.mLayer >= mService.mSystemDecorLayer) {
// Above the decor layer is easy, just use the entire window.
// Unless we have a universe background... in which case all the
@@ -1106,8 +1133,9 @@
"SIZE " + width + "x" + height, null);
mSurfaceResized = true;
mSurface.setSize(width, height);
- mAnimator.mPendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = w.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo();
mService.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
@@ -1360,7 +1388,7 @@
// do a layout. If called from within the transaction
// loop, this will cause it to restart with a new
// layout.
- mService.mLayoutNeeded = true;
+ c.mDisplayContent.layoutNeeded = true;
}
}
}
@@ -1429,8 +1457,8 @@
} else {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
-
applyAnimationLocked(transit, true);
+ mService.scheduleNotifyWindowTranstionIfNeededLocked(mWin, transit);
}
// TODO(cmautner): Move back to WindowState?
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 43e59b2..d097a93 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -4,7 +4,6 @@
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
- com_android_server_display_SurfaceFlingerDisplayAdapter.cpp \
com_android_server_input_InputApplicationHandle.cpp \
com_android_server_input_InputManagerService.cpp \
com_android_server_input_InputWindowHandle.cpp \
diff --git a/services/jni/com_android_server_display_SurfaceFlingerDisplayAdapter.cpp b/services/jni/com_android_server_display_SurfaceFlingerDisplayAdapter.cpp
deleted file mode 100644
index 05a74d3..0000000
--- a/services/jni/com_android_server_display_SurfaceFlingerDisplayAdapter.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "SurfaceFlingerDisplayAdapter"
-
-#include "JNIHelp.h"
-#include "jni.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayInfo.h>
-
-#include <utils/Log.h>
-
-namespace android {
-
-static struct {
- jfieldID width;
- jfieldID height;
- jfieldID refreshRate;
- jfieldID densityDpi;
- jfieldID xDpi;
- jfieldID yDpi;
-} gDisplayDeviceInfoClassInfo;
-
-
-static void nativeGetDefaultDisplayDeviceInfo(JNIEnv* env, jclass clazz, jobject infoObj) {
- DisplayInfo info;
- status_t err = SurfaceComposerClient::getDisplayInfo(0, &info);
- if (err < 0) {
- jniThrowExceptionFmt(env, "java/lang/RuntimeException",
- "Could not get display info. err=%d", err);
- return;
- }
-
- env->SetIntField(infoObj, gDisplayDeviceInfoClassInfo.width, info.w);
- env->SetIntField(infoObj, gDisplayDeviceInfoClassInfo.height, info.h);
- env->SetFloatField(infoObj, gDisplayDeviceInfoClassInfo.refreshRate, info.fps);
- env->SetIntField(infoObj, gDisplayDeviceInfoClassInfo.densityDpi,
- (jint)((info.density*160) + .5f));
- env->SetFloatField(infoObj, gDisplayDeviceInfoClassInfo.xDpi, info.xdpi);
- env->SetFloatField(infoObj, gDisplayDeviceInfoClassInfo.yDpi, info.ydpi);
-}
-
-
-static JNINativeMethod gSurfaceFlingerDisplayAdapterMethods[] = {
- /* name, signature, funcPtr */
- { "nativeGetDefaultDisplayDeviceInfo",
- "(Lcom/android/server/display/DisplayDeviceInfo;)V",
- (void*) nativeGetDefaultDisplayDeviceInfo },
-};
-
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className);
-
-#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
- var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-
-int register_android_server_display_SurfaceFlingerDisplayAdapter(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env,
- "com/android/server/display/SurfaceFlingerDisplayAdapter",
- gSurfaceFlingerDisplayAdapterMethods, NELEM(gSurfaceFlingerDisplayAdapterMethods));
- LOG_FATAL_IF(res < 0, "Unable to register native methods.");
-
- jclass clazz;
- FIND_CLASS(clazz, "com/android/server/display/DisplayDeviceInfo");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.width, clazz, "width", "I");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.height, clazz, "height", "I");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.refreshRate, clazz, "refreshRate", "F");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.densityDpi, clazz, "densityDpi", "I");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.xDpi, clazz, "xDpi", "F");
- GET_FIELD_ID(gDisplayDeviceInfoClassInfo.yDpi, clazz, "yDpi", "F");
- return 0;
-}
-
-} /* namespace android */
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 50873fc..423ebd1 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -22,7 +22,6 @@
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
-int register_android_server_display_SurfaceFlingerDisplayAdapter(JNIEnv* env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
@@ -52,7 +51,6 @@
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
- register_android_server_display_SurfaceFlingerDisplayAdapter(env);
register_android_server_InputApplicationHandle(env);
register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 3373fd4..a2104bb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -880,7 +880,8 @@
private Future<Void> expectClearNotifications() throws Exception {
final FutureAnswer future = new FutureAnswer();
- mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt());
+ mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt(),
+ UserHandle.myUserId());
expectLastCall().andAnswer(future).anyTimes();
return future;
}
@@ -888,7 +889,7 @@
private Future<String> expectEnqueueNotification() throws Exception {
final FutureCapture<String> tag = new FutureCapture<String>();
mNotifManager.enqueueNotificationWithTag(isA(String.class), capture(tag.capture), anyInt(),
- isA(Notification.class), isA(int[].class));
+ isA(Notification.class), isA(int[].class), UserHandle.myUserId());
return tag;
}
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index a80207e..581efc2 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -59,22 +59,16 @@
/**
* Get signal level as an int from 0..4
- *
- * @hide
*/
public abstract int getLevel();
/**
* Get the signal level as an asu value between 0..31, 99 is unknown
- *
- * @hide
*/
public abstract int getAsuLevel();
/**
* Get the signal strength as dBm
- *
- * @hide
*/
public abstract int getDbm();
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 20f713b..3912629 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -113,9 +113,7 @@
}
/**
- * Get LTE as level 0..4
- *
- * @hide
+ * Get signal level as an int from 0..4
*/
@Override
public int getLevel() {
@@ -140,8 +138,6 @@
/**
* Get the LTE signal level as an asu value between 0..97, 99 is unknown
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
- *
- * @hide
*/
@Override
public int getAsuLevel() {
@@ -172,8 +168,6 @@
/**
* Get cdma as level 0..4
- *
- * @hide
*/
public int getCdmaLevel() {
final int cdmaDbm = getCdmaDbm();
@@ -201,8 +195,6 @@
/**
* Get Evdo as level 0..4
- *
- * @hide
*/
public int getEvdoLevel() {
int evdoDbm = getEvdoDbm();
@@ -228,9 +220,7 @@
}
/**
- * Get as dBm
- *
- * @hide
+ * Get the signal strength as dBm
*/
@Override
public int getDbm() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index a311c3c..30b444b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -100,9 +100,7 @@
}
/**
- * Get LTE as level 0..4
- *
- * @hide
+ * Get signal level as an int from 0..4
*/
@Override
public int getLevel() {
@@ -123,9 +121,7 @@
}
/**
- * Get LTE as dBm
- *
- * @hide
+ * Get the signal strength as dBm
*/
@Override
public int getDbm() {
@@ -145,8 +141,6 @@
/**
* Get the LTE signal level as an asu value between 0..97, 99 is unknown
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
- *
- * @hide
*/
@Override
public int getAsuLevel() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 078699f..7a4d626 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -134,9 +134,7 @@
}
/**
- * Get LTE as level 0..4
- *
- * @hide
+ * Get signal level as an int from 0..4
*/
@Override
public int getLevel() {
@@ -170,9 +168,7 @@
}
/**
- * Get LTE as dBm
- *
- * @hide
+ * Get signal strength as dBm
*/
@Override
public int getDbm() {
@@ -182,8 +178,6 @@
/**
* Get the LTE signal level as an asu value between 0..97, 99 is unknown
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
- *
- * @hide
*/
@Override
public int getAsuLevel() {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 6047cda..5c9282e 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -40,6 +40,7 @@
import android.os.Looper;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@@ -311,8 +312,14 @@
}
@Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- BroadcastReceiver resultReceiver, Handler scheduler,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
throw new UnsupportedOperationException();
}
@@ -335,6 +342,24 @@
}
@Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent,
+ UserHandle user, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
throw new UnsupportedOperationException();
}
@@ -345,6 +370,13 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
throw new UnsupportedOperationException();
@@ -497,13 +529,18 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isRestricted() {
throw new UnsupportedOperationException();
}
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
throw new UnsupportedOperationException();
}
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 562f286..19eb8d2 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -226,6 +226,12 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public ResolveInfo resolveService(Intent intent, int flags) {
throw new UnsupportedOperationException();
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 9599b19..2348e99 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -188,7 +188,8 @@
menu.add("Send to user 1!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
Intent intent = new Intent(ActivityTestMain.this, UserTarget.class);
- sendOrderedBroadcastAsUser(intent, new UserHandle(1), new BroadcastResultReceiver(),
+ sendOrderedBroadcastAsUser(intent, new UserHandle(1), null,
+ new BroadcastResultReceiver(),
null, Activity.RESULT_OK, null, null);
return true;
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c783ad6..aebc594 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -33,6 +33,15 @@
<meta-data android:name="android.graphics.renderThread" android:value="true" />
<activity
+ android:name="PathOffsetActivity"
+ android:label="_PathOffset">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="TextPathActivity"
android:label="_TextPath">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
index e8cdbc3..7ea2a62 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
@@ -113,6 +113,12 @@
canvas.rotate(45);
canvas.drawRect(0, 0, 20, 10, p);
canvas.restore();
+ canvas.save();
+ canvas.translate(mOffset + 280, yOffset);
+ canvas.scale(0.5f, 8);
+ canvas.rotate(0.5f);
+ canvas.drawRect(0, 0, 80, 5, p);
+ canvas.restore();
canvas.restore();
yOffset += 100;
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
new file mode 100644
index 0000000..fa73de1
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathOffsetActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final PathsView view = new PathsView(this);
+ setContentView(view);
+ }
+
+ public class PathsView extends View {
+ private Path mPath;
+ private Paint mPaint;
+
+ public PathsView(Context context) {
+ super(context);
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(3);
+
+ mPath = new Path();
+ mPath.lineTo(100, 100);
+ mPath.lineTo(200, 300);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mPath.offset(1, 1);
+ mPaint.setColor(Color.RED);
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ invalidate();
+ return super.onTouchEvent(event);
+ }
+ }
+}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index f26edc6..e39d53c 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -54,9 +54,9 @@
private static final String TAG = "MemoryUsageInstrumentation";
private static final String KEY_APPS = "apps";
- private Map<String, Intent> nameToIntent;
- private Map<String, String> nameToProcess;
- private Map<String, String> nameToResultKey;
+ private Map<String, Intent> mNameToIntent;
+ private Map<String, String> mNameToProcess;
+ private Map<String, String> mNameToResultKey;
public void testMemory() {
MemoryUsageInstrumentation instrumentation =
@@ -67,7 +67,7 @@
parseArgs(args);
Bundle results = new Bundle();
- for (String app : nameToResultKey.keySet()) {
+ for (String app : mNameToResultKey.keySet()) {
String processName;
try {
processName = startApp(app);
@@ -81,7 +81,7 @@
}
private void parseArgs(Bundle args) {
- nameToResultKey = new HashMap<String, String>();
+ mNameToResultKey = new HashMap<String, String>();
String appList = args.getString(KEY_APPS);
if (appList == null)
@@ -95,13 +95,13 @@
fail();
}
- nameToResultKey.put(parts[0], parts[1]);
+ mNameToResultKey.put(parts[0], parts[1]);
}
}
private void createMappings() {
- nameToIntent = new HashMap<String, Intent>();
- nameToProcess = new HashMap<String, String>();
+ mNameToIntent = new HashMap<String, Intent>();
+ mNameToProcess = new HashMap<String, String>();
PackageManager pm = getInstrumentation().getContext()
.getPackageManager();
@@ -120,8 +120,8 @@
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startIntent.setClassName(ri.activityInfo.packageName,
ri.activityInfo.name);
- nameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
- nameToProcess.put(ri.loadLabel(pm).toString(),
+ mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
+ mNameToProcess.put(ri.loadLabel(pm).toString(),
ri.activityInfo.processName);
}
}
@@ -130,11 +130,11 @@
private String startApp(String appName) throws NameNotFoundException {
Log.i(TAG, "Starting " + appName);
- if (!nameToProcess.containsKey(appName))
+ if (!mNameToProcess.containsKey(appName))
throw new NameNotFoundException("Could not find: " + appName);
- String process = nameToProcess.get(appName);
- Intent startIntent = nameToIntent.get(appName);
+ String process = mNameToProcess.get(appName);
+ Intent startIntent = mNameToIntent.get(appName);
getInstrumentation().getContext().startActivity(startIntent);
return process;
}
@@ -154,14 +154,14 @@
}
pssData.add(pss);
if (iteration >= MIN_ITERATIONS && stabilized(pssData)) {
- results.putInt(nameToResultKey.get(appName), pss);
+ results.putInt(mNameToResultKey.get(appName), pss);
return;
}
iteration++;
}
Log.w(TAG, appName + " memory usage did not stabilize");
- results.putInt(appName, average(pssData));
+ results.putInt(mNameToResultKey.get(appName), average(pssData));
}
private int average(List<Integer> pssData) {
@@ -202,12 +202,12 @@
continue;
Log.w(TAG, appName + " crashed: " + crash.shortMsg);
- results.putString(nameToResultKey.get(appName), crash.shortMsg);
+ results.putString(mNameToResultKey.get(appName), crash.shortMsg);
return;
}
}
- results.putString(nameToResultKey.get(appName),
+ results.putString(mNameToResultKey.get(appName),
"Crashed for unknown reason");
Log.w(TAG, appName
+ " not found in process list, most likely it is crashed");
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
new file mode 100644
index 0000000..87a2de1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class ColorMatrix extends TestBase {
+ private ScriptC_colormatrix mScript;
+ private ScriptIntrinsicColorMatrix mIntrinsic;
+ private boolean mUseIntrinsic;
+
+ public ColorMatrix(boolean useIntrinsic) {
+ mUseIntrinsic = useIntrinsic;
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ Matrix4f m = new Matrix4f();
+ m.set(1, 0, 0.2f);
+ m.set(1, 1, 0.9f);
+ m.set(1, 2, 0.2f);
+
+ if (mUseIntrinsic) {
+ mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+ mIntrinsic.setColorMatrix(m);
+ } else {
+ mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+ mScript.invoke_setMatrix(m);
+ }
+ }
+
+ public void runTest() {
+ if (mUseIntrinsic) {
+ mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+ } else {
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java
new file mode 100644
index 0000000..51794db
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class Convolve3x3 extends TestBase {
+ private ScriptC_convolve3x3 mScript;
+ private ScriptIntrinsicConvolve3x3 mIntrinsic;
+
+ private int mWidth;
+ private int mHeight;
+ private boolean mUseIntrinsic;
+
+ public Convolve3x3(boolean useIntrinsic) {
+ mUseIntrinsic = useIntrinsic;
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ mWidth = mInPixelsAllocation.getType().getX();
+ mHeight = mInPixelsAllocation.getType().getY();
+
+ float f[] = new float[9];
+ f[0] = 0.f; f[1] = -1.f; f[2] = 0.f;
+ f[3] = -1.f; f[4] = 5.f; f[5] = -1.f;
+ f[6] = 0.f; f[7] = -1.f; f[8] = 0.f;
+
+ if (mUseIntrinsic) {
+ mIntrinsic = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+ mIntrinsic.setColorMatrix(f);
+ mIntrinsic.setInput(mInPixelsAllocation);
+ } else {
+ mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+ mScript.set_gCoeffs(f);
+ mScript.set_gIn(mInPixelsAllocation);
+ mScript.set_gWidth(mWidth);
+ mScript.set_gHeight(mHeight);
+ }
+ }
+
+ public void runTest() {
+ if (mUseIntrinsic) {
+ mIntrinsic.forEach(mOutPixelsAllocation);
+ } else {
+ mScript.forEach_root(mOutPixelsAllocation);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java
new file mode 100644
index 0000000..efca0b5
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class Copy extends TestBase {
+ private ScriptC_copy mScript;
+
+ public void createTest(android.content.res.Resources res) {
+ mScript = new ScriptC_copy(mRS, res, R.raw.copy);
+ }
+
+ public void runTest() {
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 07626a3..001dea8 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -168,13 +168,25 @@
mTest = new Vignette(true, true);
break;
case 15:
- mTest = new GroupTest(true);
- break;
- case 16:
mTest = new GroupTest(false);
break;
+ case 16:
+ mTest = new GroupTest(true);
+ break;
case 17:
- mTest = new Intrinsics(0);
+ mTest = new Convolve3x3(false);
+ break;
+ case 18:
+ mTest = new Convolve3x3(true);
+ break;
+ case 19:
+ mTest = new ColorMatrix(false);
+ break;
+ case 20:
+ mTest = new ColorMatrix(true);
+ break;
+ case 21:
+ mTest = new Copy();
break;
}
@@ -188,7 +200,7 @@
}
void setupTests() {
- mTestNames = new String[18];
+ mTestNames = new String[22];
mTestNames[0] = "Levels Vec3 Relaxed";
mTestNames[1] = "Levels Vec4 Relaxed";
mTestNames[2] = "Levels Vec3 Full";
@@ -206,7 +218,11 @@
mTestNames[14] = "Vignette Approximate Relaxed";
mTestNames[15] = "Group Test (emulated)";
mTestNames[16] = "Group Test (native)";
- mTestNames[17] = "Intrinsics Convolve 3x3";
+ mTestNames[17] = "Convolve 3x3";
+ mTestNames[18] = "Intrinsics Convolve 3x3";
+ mTestNames[19] = "ColorMatrix";
+ mTestNames[20] = "Intrinsics ColorMatrix";
+ mTestNames[21] = "Copy";
mTestSpinner.setAdapter(new ArrayAdapter<String>(
this, R.layout.spinner_layout, mTestNames));
}
@@ -280,14 +296,14 @@
// button hook
public void benchmark(View v) {
- long t = getBenchmark();
+ float t = getBenchmark();
//long javaTime = javaFilter();
//mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
mBenchmarkResult.setText("Result: " + t + " ms");
}
// For benchmark test
- public long getBenchmark() {
+ public float getBenchmark() {
mDoingBenchmark = true;
mTest.setupBenchmark();
@@ -303,14 +319,18 @@
Log.v(TAG, "Benchmarking");
t = java.lang.System.currentTimeMillis();
- mTest.runTest();
+ for (int i=0; i<10; i++) {
+ mTest.runTest();
+ }
mTest.finish();
t = java.lang.System.currentTimeMillis() - t;
+ float ft = (float)t;
+ ft /= 10;
- Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
+ Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + ft);
mTest.exitBenchmark();
mDoingBenchmark = false;
- return t;
+ return ft;
}
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
index 912d863..f995437 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
@@ -78,7 +78,7 @@
BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
for (int i = 0; i < ITERATION; i++ ) {
- t = mAct.getBenchmark();
+ t = (long)mAct.getBenchmark();
sum += t;
rsWriter.write("Renderscript frame time core: " + t + " ms\n");
Log.v(TAG, "RenderScript framew time core: " + t + " ms");
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
deleted file mode 100644
index dab8111..0000000
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.rs.image;
-
-import java.lang.Math;
-
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.renderscript.Script;
-import android.renderscript.ScriptIntrinsicConvolve3x3;
-import android.renderscript.Type;
-import android.util.Log;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-public class Intrinsics extends TestBase {
- private ScriptIntrinsicConvolve3x3 mScript;
-
- Intrinsics(int id) {
- }
-
- public boolean onBar1Setup(SeekBar b, TextView t) {
- t.setText("Strength");
- b.setProgress(50);
- return true;
- }
-
- public void onBar1Changed(int progress) {
- float s = progress / 100.0f;
- float v[] = new float[9];
- v[0] = 0.f; v[1] = -s; v[2] = 0.f;
- v[3] = -s; v[4] = s*4+1; v[5] = -s;
- v[6] = 0.f; v[7] = -s; v[8] = 0.f;
- mScript.setColorMatrix(v);
- }
-
-
- public void createTest(android.content.res.Resources res) {
- mScript = ScriptIntrinsicConvolve3x3.create(mRS, Element.RGBA_8888(mRS));
- }
-
- public void runTest() {
- mScript.setInput(mInPixelsAllocation);
- mScript.forEach(mOutPixelsAllocation);
- }
-
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
index b6b4a0f..455fcc2 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
@@ -25,9 +25,9 @@
float gCoeffs[9];
void root(uchar4 *out, uint32_t x, uint32_t y) {
- uint32_t x1 = min((int32_t)x+1, gWidth);
+ uint32_t x1 = min((int32_t)x+1, gWidth-1);
uint32_t x2 = max((int32_t)x-1, 0);
- uint32_t y1 = min((int32_t)y+1, gHeight);
+ uint32_t y1 = min((int32_t)y+1, gHeight-1);
uint32_t y2 = max((int32_t)y-1, 0);
float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs
new file mode 100644
index 0000000..9eb5d43
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+
+void root(const uchar4 *v_in, uchar4 *v_out) {
+ *v_out = *v_in;
+}
+
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 70e2aac..ec39aab 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Vibrator;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.Log;
import android.net.Uri;
import android.os.SystemClock;
@@ -798,7 +799,8 @@
null,
100,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
@@ -822,7 +824,8 @@
null,
200,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
@@ -846,7 +849,8 @@
null,
1,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 46b8a27..5b71adc 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -183,6 +183,13 @@
return 0;
}
+ // layout direction
+ if (getLayoutDirectionName(part.string(), &config)) {
+ *axis = AXIS_LAYOUTDIR;
+ *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+ return 0;
+ }
+
// smallest screen dp width
if (getSmallestScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SMALLESTSCREENWIDTHDP;
@@ -309,6 +316,8 @@
case AXIS_LANGUAGE:
return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
| (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ case AXIS_LAYOUTDIR:
+ return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
case AXIS_SCREENLAYOUTSIZE:
return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
case AXIS_ORIENTATION:
@@ -364,7 +373,7 @@
Vector<String8> parts;
String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, vers;
+ String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
const char *p = dir;
@@ -452,6 +461,18 @@
//printf("not region: %s\n", part.string());
}
+ if (getLayoutDirectionName(part.string())) {
+ layoutDir = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not layout direction: %s\n", part.string());
+ }
+
if (getSmallestScreenWidthDpName(part.string())) {
smallestwidthdp = part;
@@ -674,6 +695,7 @@
this->navHidden = navHidden;
this->navigation = nav;
this->screenSize = size;
+ this->layoutDirection = layoutDir;
this->version = vers;
// what is this anyway?
@@ -691,6 +713,8 @@
s += ",";
s += this->locale;
s += ",";
+ s += layoutDirection;
+ s += ",";
s += smallestScreenWidthDp;
s += ",";
s += screenWidthDp;
@@ -747,6 +771,12 @@
}
s += locale;
}
+ if (this->layoutDirection != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += layoutDirection;
+ }
if (this->smallestScreenWidthDp != "") {
if (s.length() > 0) {
s += "-";
@@ -958,6 +988,28 @@
return false;
}
+bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ldltr") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "ldrtl") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
+
+ return false;
+}
+
bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
ResTable_config* out)
{
@@ -1415,6 +1467,7 @@
int v = mcc.compare(o.mcc);
if (v == 0) v = mnc.compare(o.mnc);
if (v == 0) v = locale.compare(o.locale);
+ if (v == 0) v = layoutDirection.compare(o.layoutDirection);
if (v == 0) v = vendor.compare(o.vendor);
if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
@@ -1447,6 +1500,7 @@
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
getLocaleName(locale.string(), ¶ms);
+ getLayoutDirectionName(layoutDirection.string(), ¶ms);
getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
getScreenWidthDpName(screenWidthDp.string(), ¶ms);
getScreenHeightDpName(screenHeightDp.string(), ¶ms);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index d5f296c..5cfa913 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -51,6 +51,7 @@
AXIS_SMALLESTSCREENWIDTHDP,
AXIS_SCREENWIDTHDP,
AXIS_SCREENHEIGHTDP,
+ AXIS_LAYOUTDIR,
AXIS_VERSION,
AXIS_START = AXIS_MCC,
@@ -95,6 +96,7 @@
static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
+ static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
int compare(const AaptGroupEntry& o) const;
@@ -133,6 +135,7 @@
String8 navHidden;
String8 navigation;
String8 screenSize;
+ String8 layoutDirection;
String8 version;
mutable bool mParamsChanged;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index d98fe65..3d7b088 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2811,7 +2811,7 @@
NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
config.mcc, config.mnc,
config.language[0] ? config.language[0] : '-',
@@ -2829,7 +2829,8 @@
config.screenHeight,
config.smallestScreenWidthDp,
config.screenWidthDp,
- config.screenHeightDp));
+ config.screenHeightDp,
+ config.layoutDirection));
if (filterable && !filter.match(config)) {
continue;
@@ -2853,7 +2854,7 @@
tHeader->config = config;
NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
tHeader->config.mcc, tHeader->config.mnc,
tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2871,7 +2872,8 @@
tHeader->config.screenHeight,
tHeader->config.smallestScreenWidthDp,
tHeader->config.screenWidthDp,
- tHeader->config.screenHeightDp));
+ tHeader->config.screenHeightDp,
+ tHeader->config.layoutDirection));
tHeader->config.swapHtoD();
// Build the entries inside of this type.
@@ -3489,7 +3491,7 @@
if (config != NULL) {
NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
sourcePos.file.string(), sourcePos.line,
config->mcc, config->mnc,
config->language[0] ? config->language[0] : '-',
@@ -3506,7 +3508,8 @@
config->screenHeight,
config->smallestScreenWidthDp,
config->screenWidthDp,
- config->screenHeightDp));
+ config->screenHeightDp,
+ config->layoutDirection));
} else {
NOISY(printf("New entry at %s:%d: NULL config\n",
sourcePos.file.string(), sourcePos.line));
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index bd332a6..26cb97b 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -31,6 +31,7 @@
*/
public class SystemClock_Delegate {
private static long sBootTime = System.currentTimeMillis();
+ private static long sBootTimeNano = System.nanoTime();
@LayoutlibDelegate
/*package*/ static boolean setCurrentTimeMillis(long millis) {
@@ -60,6 +61,16 @@
}
/**
+ * Returns nanoseconds since boot, including time spent in sleep.
+ *
+ * @return elapsed nanoseconds since boot.
+ */
+ @LayoutlibDelegate
+ /*package*/ static long elapsedRealtimeNano() {
+ return System.nanoTime() - sBootTimeNano;
+ }
+
+ /**
* Returns milliseconds running in the current thread.
*
* @return elapsed milliseconds in the thread
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
index 1df78c2..8b4c60b 100644
--- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -91,4 +91,42 @@
/*package*/ static float sqrt(float value) {
return (float)Math.sqrt(value);
}
+
+ /**
+ * Returns the closest float approximation of the raising "e" to the power
+ * of the argument.
+ *
+ * @param value to compute the exponential of
+ * @return the exponential of value
+ */
+ @LayoutlibDelegate
+ /*package*/ static float exp(float value) {
+ return (float)Math.exp(value);
+ }
+
+ /**
+ * Returns the closest float approximation of the result of raising {@code
+ * x} to the power of {@code y}.
+ *
+ * @param x the base of the operation.
+ * @param y the exponent of the operation.
+ * @return {@code x} to the power of {@code y}.
+ */
+ @LayoutlibDelegate
+ /*package*/ static float pow(float x, float y) {
+ return (float)Math.pow(x, y);
+ }
+
+ /**
+ * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+ * {@code y}</i><sup>{@code 2}</sup>{@code )}.
+ *
+ * @param x a float number
+ * @param y a float number
+ * @return the hypotenuse
+ */
+ @LayoutlibDelegate
+ /*package*/ static float hypot(float x, float y) {
+ return (float)Math.sqrt(x*x + y*y);
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 76033d4..428c4c2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -67,6 +67,7 @@
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -925,6 +926,12 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ // pass
+ return null;
+ }
+
+ @Override
public String[] databaseList() {
// pass
return null;
@@ -1157,6 +1164,13 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
+ IntentFilter arg1, String arg2, Handler arg3) {
+ // pass
+ return null;
+ }
+
+ @Override
public void removeStickyBroadcast(Intent arg0) {
// pass
@@ -1200,8 +1214,14 @@
}
@Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission) {
+ // pass
+ }
+
+ @Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- BroadcastReceiver resultReceiver, Handler scheduler,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
int initialCode, String initialData, Bundle initialExtras) {
// pass
}
@@ -1220,6 +1240,24 @@
}
@Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ // pass
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent,
+ UserHandle user, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ // pass
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ // pass
+ }
+
+ @Override
public void setTheme(int arg0) {
// pass
@@ -1333,7 +1371,7 @@
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
// pass
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 5516339..5d64e8a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -22,22 +22,21 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.Display_Delegate;
import android.view.Gravity;
import android.view.IApplicationToken;
+import android.view.IDisplayContentChangeListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.WindowInfo;
import java.util.List;
@@ -293,7 +292,6 @@
@Override
public void setAppOrientation(IApplicationToken arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -301,7 +299,6 @@
CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9)
throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -313,19 +310,16 @@
@Override
public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void setFocusedApp(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -341,13 +335,11 @@
@Override
public void setInTouchMode(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void setNewConfiguration(Configuration arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -358,19 +350,16 @@
@Override
public void setStrictModeVisualIndicatorPreference(String arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void showStrictModeViolation(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void startAppFreezingScreen(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -382,13 +371,11 @@
@Override
public void statusBarVisibilityChanged(int arg0) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -400,7 +387,6 @@
@Override
public void thawRotation() throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -453,12 +439,6 @@
}
@Override
- public boolean getWindowFrame(IBinder token, Rect outBounds) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
public float getWindowCompatibilityScale(IBinder windowToken) throws RemoteException {
// TODO Auto-generated method stub
return 0;
@@ -468,4 +448,34 @@
public void setInputFilter(IInputFilter filter) throws RemoteException {
// TODO Auto-generated method stub
}
+
+ @Override
+ public void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void addDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void removeDisplayContentChangeListener(int displayId,
+ IDisplayContentChangeListener listener) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public WindowInfo getWindowInfo(IBinder token) throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void getVisibleWindowsForDisplay(int displayId, List<WindowInfo> outInfos)
+ throws RemoteException {
+ // TODO Auto-generated method stub
+ }
}
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 691eca7..67b0a9c 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
@@ -194,4 +194,9 @@
// pass for now.
return null;
}
+
+ @Override
+ public void onRectangleOnScreenRequested(IBinder window, Rect rectangle, boolean immediate) {
+ // pass for now.
+ }
}
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
index 29166dc..9fcd21d 100644
--- a/voip/java/android/net/sip/SimpleSessionDescription.java
+++ b/voip/java/android/net/sip/SimpleSessionDescription.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Locale;
/**
* An object used to manipulate messages of Session Description Protocol (SDP).
@@ -66,7 +67,7 @@
public SimpleSessionDescription(long sessionId, String address) {
address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
mFields.parse("v=0");
- mFields.parse(String.format("o=- %d %d %s", sessionId,
+ mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
System.currentTimeMillis(), address));
mFields.parse("s=-");
mFields.parse("t=0 0");
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 6b08074..55de065 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -106,5 +106,7 @@
Messenger getWifiStateMachineMessenger();
String getConfigFile();
+
+ void captivePortalCheckComplete();
}
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index 6aeac5f..d1e9b67 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -25,6 +25,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.util.Log;
/**
@@ -145,7 +146,7 @@
WifiManager.EXTRA_SUPPLICANT_ERROR,
WifiManager.ERROR_AUTHENTICATING);
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
/********************************************************
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index e9f3480..a2332e3 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -36,6 +36,7 @@
import android.os.Message;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -601,7 +602,7 @@
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
@@ -611,7 +612,7 @@
Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, true);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
void loadConfiguredNetworks() {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 284bee8..aa59158 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1971,4 +1971,11 @@
return false;
}
}
+
+ /** @hide */
+ public void captivePortalCheckComplete() {
+ try {
+ mService.captivePortalCheckComplete();
+ } catch (RemoteException e) {}
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index a447c86..17c930b 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -20,6 +20,8 @@
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pService;
+import android.net.wifi.p2p.WifiP2pService.P2pStatus;
import android.net.wifi.p2p.WifiP2pProvDiscEvent;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.net.wifi.StateChangeResult;
@@ -186,7 +188,7 @@
/* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
[psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
- go_dev_addr=fa:7b:7a:42:02:13 */
+ go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] */
private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
/* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
@@ -594,7 +596,13 @@
if (tokens.length != 2) return;
String[] nameValue = tokens[1].split("=");
if (nameValue.length != 2) return;
- mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
+ P2pStatus err = P2pStatus.UNKNOWN;
+ try {
+ err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err);
} else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
new WifiP2pProvDiscEvent(dataString));
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 1b7e378..805faa6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -500,6 +500,14 @@
}
}
+ public boolean setWfdEnable(boolean enable) {
+ return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
+ }
+
+ public boolean setWfdDeviceInfo(String hex) {
+ return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
+ }
+
/**
* "sta" prioritizes STA connection over P2P and "p2p" prioritizes
* P2P connection over STA
@@ -569,10 +577,9 @@
break;
}
- //TODO: Add persist behavior once the supplicant interaction is fixed for both
- // group and client scenarios
- /* Persist unless there is an explicit request to not do so*/
- //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+ if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+ args.add("persistent");
+ }
if (joinExistingGroup) {
args.add("join");
@@ -614,10 +621,17 @@
return false;
}
- public boolean p2pGroupAdd() {
+ public boolean p2pGroupAdd(boolean persistent) {
+ if (persistent) {
+ return doBooleanCommand("P2P_GROUP_ADD persistent");
+ }
return doBooleanCommand("P2P_GROUP_ADD");
}
+ public boolean p2pGroupAdd(int netId) {
+ return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
+ }
+
public boolean p2pGroupRemove(String iface) {
if (TextUtils.isEmpty(iface)) return false;
return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
@@ -646,6 +660,9 @@
return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
}
+ public String p2pGetSsid(String deviceAddress) {
+ return p2pGetParam(deviceAddress, "oper_ssid");
+ }
public String p2pGetDeviceAddress() {
String status = status();
@@ -687,6 +704,24 @@
return doStringCommand("P2P_PEER " + deviceAddress);
}
+ private String p2pGetParam(String deviceAddress, String key) {
+ if (deviceAddress == null) return null;
+
+ String peerInfo = p2pPeer(deviceAddress);
+ if (peerInfo == null) return null;
+ String[] tokens= peerInfo.split("\n");
+
+ key += "=";
+ for (String token : tokens) {
+ if (token.startsWith(key)) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) break;
+ return nameValue[1];
+ }
+ }
+ return null;
+ }
+
public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
/*
* P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index a5322fa..40111fa 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -68,6 +68,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
@@ -256,6 +257,8 @@
static final int CMD_DELAYED_STOP_DRIVER = BASE + 18;
/* A delayed message sent to start driver when it fail to come up */
static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
+ /* Ready to switch to network as default */
+ static final int CMD_CAPTIVE_CHECK_COMPLETE = BASE + 20;
/* Start the soft access point */
static final int CMD_START_AP = BASE + 21;
@@ -459,6 +462,8 @@
private State mObtainingIpState = new ObtainingIpState();
/* Waiting for link quality verification to be complete */
private State mVerifyingLinkState = new VerifyingLinkState();
+ /* Waiting for captive portal check to be complete */
+ private State mCaptivePortalCheckState = new CaptivePortalCheckState();
/* Connected with IP addr */
private State mConnectedState = new ConnectedState();
/* disconnect issued, waiting for network disconnect confirmation */
@@ -695,6 +700,7 @@
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mVerifyingLinkState, mL2ConnectedState);
+ addState(mCaptivePortalCheckState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
@@ -865,6 +871,10 @@
}
}
+ public void captivePortalCheckComplete() {
+ sendMessage(obtainMessage(CMD_CAPTIVE_CHECK_COMPLETE));
+ }
+
/**
* TODO: doc
*/
@@ -1335,7 +1345,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void setWifiApState(int wifiApState) {
@@ -1360,7 +1370,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private static final String BSSID_STR = "bssid=";
@@ -1567,14 +1577,14 @@
private void sendScanResultsAvailableBroadcast() {
Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendRssiChangeBroadcast(final int newRssi) {
Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendNetworkStateChangeBroadcast(String bssid) {
@@ -1588,21 +1598,21 @@
mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendLinkConfigurationChangedBroadcast() {
Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
@@ -1616,7 +1626,7 @@
}
if (state != mNetworkInfo.getDetailedState()) {
- mNetworkInfo.setDetailedState(state, null, null);
+ mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
}
}
@@ -1663,10 +1673,7 @@
/* In case we were in middle of DHCP operation
restore back powermode */
handlePostDhcpSetup();
-
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
- mDhcpStateMachine.doQuit();
- mDhcpStateMachine = null;
}
try {
@@ -1919,6 +1926,9 @@
case CMD_CLEAR_SUSPEND_OPTIMIZATIONS:
case CMD_NO_NETWORKS_PERIODIC_SCAN:
break;
+ case DhcpStateMachine.CMD_ON_QUIT:
+ mDhcpStateMachine = null;
+ break;
case CMD_SET_SUSPEND_OPTIMIZATIONS:
mSuspendWakeLock.release();
break;
@@ -2489,6 +2499,9 @@
/* Send any reset commands to supplicant before shutting it down */
handleNetworkDisconnect();
+ if (mDhcpStateMachine != null) {
+ mDhcpStateMachine.doQuit();
+ }
if (DBG) log("stopping supplicant");
if (!mWifiNative.stopSupplicant()) {
@@ -3188,8 +3201,11 @@
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
//start DHCP
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
+ if (mDhcpStateMachine == null) {
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+
+ }
mDhcpStateMachine.registerForPreDhcpNotification();
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
} else {
@@ -3253,6 +3269,26 @@
//stay here
break;
case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
+ transitionTo(mCaptivePortalCheckState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class CaptivePortalCheckState extends State {
+ @Override
+ public void enter() {
+ setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_CAPTIVE_CHECK_COMPLETE:
try {
mNwService.enableIpv6(mInterfaceName);
} catch (RemoteException re) {
@@ -3260,7 +3296,6 @@
} catch (IllegalStateException e) {
loge("Failed to enable IPv6: " + e);
}
-
setNetworkDetailedState(DetailedState.CONNECTED);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
sendNetworkStateChangeBroadcast(mLastBssid);
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index bfb91e2..a5a2469 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -23,6 +23,7 @@
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
import android.net.NetworkStateTracker;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Handler;
@@ -113,6 +114,14 @@
}
/**
+ * Captive check is complete, switch to network
+ */
+ @Override
+ public void captivePortalCheckComplete() {
+ mWifiManager.captivePortalCheckComplete();
+ }
+
+ /**
* Turn the wireless radio off for a network.
* @param turnOn {@code true} to turn the radio on, {@code false}
*/
@@ -235,9 +244,10 @@
mLinkCapabilities = new LinkCapabilities();
}
// don't want to send redundent state messages
- // TODO can this be fixed in WifiStateMachine?
+ // but send portal check detailed state notice
NetworkInfo.State state = mNetworkInfo.getState();
- if (mLastState == state) {
+ if (mLastState == state &&
+ mNetworkInfo.getDetailedState() != DetailedState.CAPTIVE_PORTAL_CHECK) {
return;
} else {
mLastState = state;
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 29a53b6..7fa6aac 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -16,20 +16,15 @@
package android.net.wifi;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkInfo;
-import android.net.Uri;
import android.net.wifi.RssiPacketCountInfo;
import android.os.Message;
import android.os.SystemClock;
@@ -44,10 +39,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import java.io.IOException;
import java.io.PrintWriter;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.text.DecimalFormat;
/**
@@ -100,8 +92,7 @@
private static final int EVENT_SCREEN_OFF = BASE + 9;
/* Internal messages */
- private static final int CMD_DELAYED_WALLED_GARDEN_CHECK = BASE + 11;
- private static final int CMD_RSSI_FETCH = BASE + 12;
+ private static final int CMD_RSSI_FETCH = BASE + 11;
/* Notifications from/to WifiStateMachine */
static final int POOR_LINK_DETECTED = BASE + 21;
@@ -266,27 +257,6 @@
new MaxAvoidTime( 0 * 60000, -55 ),
};
-
- private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden";
-
- private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
-
- /**
- * See http://go/clientsdns for usage approval
- */
- private static final String DEFAULT_WALLED_GARDEN_URL =
- "http://clients3.google.com/generate_204";
- private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
-
- /**
- * Some carrier apps might have support captive portal handling. Add some
- * delay to allow app authentication to be done before our test. TODO: This
- * should go away once we provide an API to apps to disable walled garden
- * test for certain SSIDs
- */
- private static final int WALLED_GARDEN_START_DELAY_MS = 3000;
-
-
/* Framework related */
private Context mContext;
private ContentResolver mContentResolver;
@@ -300,13 +270,6 @@
/* System settingss related */
private static boolean sWifiOnly = false;
private boolean mPoorNetworkDetectionEnabled;
- private long mWalledGardenIntervalMs;
- private boolean mWalledGardenTestEnabled;
- private String mWalledGardenUrl;
-
- /* Wall garden detection related */
- private long mLastWalledGardenCheckTime = 0;
- private boolean mWalledGardenNotificationShown;
/* Poor link detection related */
private LruCache<String, BssidStatistics> mBssidCache =
@@ -325,7 +288,6 @@
private NotConnectedState mNotConnectedState = new NotConnectedState();
private VerifyingLinkState mVerifyingLinkState = new VerifyingLinkState();
private ConnectedState mConnectedState = new ConnectedState();
- private WalledGardenCheckState mWalledGardenCheckState = new WalledGardenCheckState();
private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
private LinkMonitoringState mLinkMonitoringState = new LinkMonitoringState();
private OnlineState mOnlineState = new OnlineState();
@@ -359,7 +321,6 @@
addState(mNotConnectedState, mWatchdogEnabledState);
addState(mVerifyingLinkState, mWatchdogEnabledState);
addState(mConnectedState, mWatchdogEnabledState);
- addState(mWalledGardenCheckState, mConnectedState);
addState(mOnlineWatchState, mConnectedState);
addState(mLinkMonitoringState, mConnectedState);
addState(mOnlineState, mConnectedState);
@@ -379,13 +340,12 @@
Context.CONNECTIVITY_SERVICE);
sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
- // Watchdog is always enabled. Poor network detection & walled garden detection
- // can individually be turned on/off
+ // Watchdog is always enabled. Poor network detection can be seperately turned on/off
// TODO: Remove this setting & clean up state machine since we always have
// watchdog in an enabled state
putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, true);
- // disable poor network avoidance, but keep watchdog active for walled garden detection
+ // disable poor network avoidance
if (sWifiOnly) {
logd("Disabling poor network avoidance for wi-fi only device");
putSettingsBoolean(contentResolver,
@@ -458,44 +418,8 @@
};
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
- false, contentObserver);
- }
-
- /**
- * DNS based detection techniques do not work at all hotspots. The one sure
- * way to check a walled garden is to see if a URL fetch on a known address
- * fetches the data we expect
- */
- private boolean isWalledGardenConnection() {
- HttpURLConnection urlConnection = null;
- try {
- URL url = new URL(mWalledGardenUrl);
- urlConnection = (HttpURLConnection) url.openConnection();
- urlConnection.setInstanceFollowRedirects(false);
- urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
- urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
- urlConnection.setUseCaches(false);
- urlConnection.getInputStream();
- // we got a valid response, but not from the real google
- return urlConnection.getResponseCode() != 204;
- } catch (IOException e) {
- if (DBG) logd("Walled garden check - probably not a portal: exception " + e);
- return false;
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
}
public void dump(PrintWriter pw) {
@@ -504,10 +428,7 @@
pw.println("mWifiInfo: [" + mWifiInfo + "]");
pw.println("mLinkProperties: [" + mLinkProperties + "]");
pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
- pw.println("mWalledGardenIntervalMs: [" + mWalledGardenIntervalMs + "]");
pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
- pw.println("mWalledGardenTestEnabled: [" + mWalledGardenTestEnabled + "]");
- pw.println("mWalledGardenUrl: [" + mWalledGardenUrl + "]");
}
private boolean isWatchdogEnabled() {
@@ -521,47 +442,6 @@
mPoorNetworkDetectionEnabled = getSettingsBoolean(mContentResolver,
Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
- mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
- mWalledGardenUrl = getSettingsStr(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL,
- DEFAULT_WALLED_GARDEN_URL);
- mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
- Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
- DEFAULT_WALLED_GARDEN_INTERVAL_MS);
- }
-
- private void setWalledGardenNotificationVisible(boolean visible) {
- // if it should be hidden and it is already hidden, then noop
- if (!visible && !mWalledGardenNotificationShown) {
- return;
- }
-
- Resources r = Resources.getSystem();
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mWalledGardenUrl));
- intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-
- CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
- CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
- mWifiInfo.getSSID());
-
- Notification notification = new Notification();
- notification.when = 0;
- notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range;
- notification.flags = Notification.FLAG_AUTO_CANCEL;
- notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
- notification.tickerText = title;
- notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
-
- notificationManager.notify(WALLED_GARDEN_NOTIFICATION_ID, 1, notification);
- } else {
- notificationManager.cancel(WALLED_GARDEN_NOTIFICATION_ID, 1);
- }
- mWalledGardenNotificationShown = visible;
}
/**
@@ -587,7 +467,6 @@
case EVENT_NETWORK_STATE_CHANGE:
case EVENT_SUPPLICANT_STATE_CHANGE:
case EVENT_BSSID_CHANGE:
- case CMD_DELAYED_WALLED_GARDEN_CHECK:
case CMD_RSSI_FETCH:
case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
@@ -685,11 +564,7 @@
}
break;
case CONNECTED:
- if (shouldCheckWalledGarden()) {
- transitionTo(mWalledGardenCheckState);
- } else {
- transitionTo(mOnlineWatchState);
- }
+ transitionTo(mOnlineWatchState);
break;
default:
transitionTo(mNotConnectedState);
@@ -716,7 +591,6 @@
return NOT_HANDLED;
}
- setWalledGardenNotificationVisible(false);
return HANDLED;
}
}
@@ -834,38 +708,6 @@
}
/**
- * Checking for wall garden.
- */
- class WalledGardenCheckState extends State {
- private int mWalledGardenToken = 0;
- @Override
- public void enter() {
- if (DBG) logd(getName());
- sendMessageDelayed(obtainMessage(CMD_DELAYED_WALLED_GARDEN_CHECK,
- ++mWalledGardenToken, 0), WALLED_GARDEN_START_DELAY_MS);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_DELAYED_WALLED_GARDEN_CHECK:
- if (msg.arg1 == mWalledGardenToken) {
- mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
- if (isWalledGardenConnection()) {
- if (DBG) logd("Walled garden detected");
- setWalledGardenNotificationVisible(true);
- }
- transitionTo(mOnlineWatchState);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- /**
* RSSI is high enough and don't need link monitoring.
*/
class OnlineWatchState extends State {
@@ -1037,22 +879,6 @@
}
}
- private boolean shouldCheckWalledGarden() {
- if (!mWalledGardenTestEnabled) {
- if (DBG) logd("Skipping walled garden check - disabled");
- return false;
- }
-
- long waitTime = (mWalledGardenIntervalMs + mLastWalledGardenCheckTime)
- - SystemClock.elapsedRealtime();
-
- if (mLastWalledGardenCheckTime != 0 && waitTime > 0) {
- if (DBG) logd("Skipping walled garden check - wait " + waitTime + " ms.");
- return false;
- }
- return true;
- }
-
private void updateCurrentBssid(String bssid) {
if (DBG) logd("Update current BSSID to " + (bssid != null ? bssid : "null"));
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6aea090..100e062 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -46,18 +46,8 @@
*/
public int groupOwnerIntent = -1;
- /**
- * Indicates whether the configuration is saved
- * @hide
- */
- public enum Persist {
- SYSTEM_DEFAULT,
- YES,
- NO
- }
-
/** @hide */
- public Persist persist = Persist.SYSTEM_DEFAULT;
+ public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
public WifiP2pConfig() {
//set defaults
@@ -110,7 +100,7 @@
sbuf.append("\n address: ").append(deviceAddress);
sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
- sbuf.append("\n persist: ").append(persist.toString());
+ sbuf.append("\n persist: ").append(netId);
return sbuf.toString();
}
@@ -125,7 +115,7 @@
deviceAddress = source.deviceAddress;
wps = new WpsInfo(source.wps);
groupOwnerIntent = source.groupOwnerIntent;
- persist = source.persist;
+ netId = source.netId;
}
}
@@ -134,7 +124,7 @@
dest.writeString(deviceAddress);
dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
- dest.writeString(persist.name());
+ dest.writeInt(netId);
}
/** Implement the Parcelable interface */
@@ -145,7 +135,7 @@
config.deviceAddress = in.readString();
config.wps = (WpsInfo) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
- config.persist = Persist.valueOf(in.readString());
+ config.netId = in.readInt();
return config;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index afdc9be..117af82 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -107,12 +107,14 @@
/** Device connection status */
public int status = UNAVAILABLE;
- /** Detailed device string pattern
+ /** @hide */
+ public WifiP2pWfdInfo wfdInfo;
+
+ /** Detailed device string pattern with WFD info
* Example:
- * P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
- * pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
- * group_capab=0x0
- *
+ * P2P-DEVICE-FOUND 00:18:6b:de:a3:6e p2p_dev_addr=00:18:6b:de:a3:6e
+ * pri_dev_type=1-0050F204-1 name='DWD-300-DEA36E' config_methods=0x188
+ * dev_capab=0x21 group_capab=0x9
*/
private static final Pattern detailedDevicePattern = Pattern.compile(
"((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) " +
@@ -231,11 +233,26 @@
return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
}
+ /** Returns true if the device is capable of invitation {@hide}*/
+ public boolean isInvitationCapable() {
+ return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0;
+ }
+
+ /** Returns true if the device reaches the limit. {@hide}*/
+ public boolean isDeviceLimit() {
+ return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0;
+ }
+
/** Returns true if the device is a group owner */
public boolean isGroupOwner() {
return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
}
+ /** Returns true if the group reaches the limit. {@hide}*/
+ public boolean isGroupLimit() {
+ return (groupCapability & GROUP_CAPAB_GROUP_LIMIT) != 0;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
@@ -258,6 +275,7 @@
sbuf.append("\n grpcapab: ").append(groupCapability);
sbuf.append("\n devcapab: ").append(deviceCapability);
sbuf.append("\n status: ").append(status);
+ sbuf.append("\n wfdInfo: ").append(wfdInfo);
return sbuf.toString();
}
@@ -277,6 +295,7 @@
deviceCapability = source.deviceCapability;
groupCapability = source.groupCapability;
status = source.status;
+ wfdInfo = source.wfdInfo;
}
}
@@ -290,6 +309,12 @@
dest.writeInt(deviceCapability);
dest.writeInt(groupCapability);
dest.writeInt(status);
+ if (wfdInfo != null) {
+ dest.writeInt(1);
+ wfdInfo.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
/** Implement the Parcelable interface */
@@ -305,6 +330,9 @@
device.deviceCapability = in.readInt();
device.groupCapability = in.readInt();
device.status = in.readInt();
+ if (in.readInt() == 1) {
+ device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in);
+ }
return device;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index c30cc73..bc492b3 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -33,6 +33,16 @@
*/
public class WifiP2pGroup implements Parcelable {
+ /** The temporary network id.
+ * {@hide} */
+ public static final int TEMPORARY_NET_ID = -1;
+
+ /** The persistent network id.
+ * If a matching persistent profile is found, use it.
+ * Otherwise, create a new persistent profile.
+ * {@hide} */
+ public static final int PERSISTENT_NET_ID = -2;
+
/** The network name */
private String mNetworkName;
@@ -50,13 +60,17 @@
private String mInterface;
+ /** The network id in the wpa_supplicant */
+ private int mNetId;
+
/** P2P group started string pattern */
private static final Pattern groupStartedPattern = Pattern.compile(
"ssid=\"(.+)\" " +
"freq=(\\d+) " +
"(?:psk=)?([0-9a-fA-F]{64})?" +
"(?:passphrase=)?(?:\"(.{8,63})\")? " +
- "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+ "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})" +
+ " ?(\\[PERSISTENT\\])?"
);
public WifiP2pGroup() {
@@ -67,13 +81,15 @@
*
* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
* [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|
- * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13
+ * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT]
*
* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
*
* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
* bssid=fa:7b:7a:42:82:13 unknown-network
*
+ * P2P-INVITATION-RECEIVED sa=b8:f9:34:2a:c7:9d persistent=0
+ *
* Note: The events formats can be looked up in the wpa_supplicant code
* @hide
*/
@@ -100,16 +116,38 @@
//String psk = match.group(3);
mPassphrase = match.group(4);
mOwner = new WifiP2pDevice(match.group(5));
-
+ if (match.group(6) != null) {
+ mNetId = PERSISTENT_NET_ID;
+ } else {
+ mNetId = TEMPORARY_NET_ID;
+ }
} else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
+ String sa = null;
+ mNetId = PERSISTENT_NET_ID;
for (String token : tokens) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) continue;
+ if (nameValue[0].equals("sa")) {
+ sa = nameValue[1];
+
+ // set source address into the client list.
+ WifiP2pDevice dev = new WifiP2pDevice();
+ dev.deviceAddress = nameValue[1];
+ mClients.add(dev);
+ continue;
+ }
+
if (nameValue[0].equals("go_dev_addr")) {
mOwner = new WifiP2pDevice(nameValue[1]);
continue;
}
+
+ if (nameValue[0].equals("persistent")) {
+ mOwner = new WifiP2pDevice(sa);
+ mNetId = Integer.parseInt(nameValue[1]);
+ continue;
+ }
}
} else {
throw new IllegalArgumentException("Malformed supplicant event");
@@ -212,6 +250,16 @@
return mInterface;
}
+ /** @hide */
+ public int getNetworkId() {
+ return mNetId;
+ }
+
+ /** @hide */
+ public void setNetworkId(int netId) {
+ this.mNetId = netId;
+ }
+
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("network: ").append(mNetworkName);
@@ -221,6 +269,7 @@
sbuf.append("\n Client: ").append(client);
}
sbuf.append("\n interface: ").append(mInterface);
+ sbuf.append("\n networkId: ").append(mNetId);
return sbuf.toString();
}
@@ -238,6 +287,7 @@
for (WifiP2pDevice d : source.getClientList()) mClients.add(d);
mPassphrase = source.getPassphrase();
mInterface = source.getInterface();
+ mNetId = source.getNetworkId();
}
}
@@ -252,6 +302,7 @@
}
dest.writeString(mPassphrase);
dest.writeString(mInterface);
+ dest.writeInt(mNetId);
}
/** Implement the Parcelable interface */
@@ -268,6 +319,7 @@
}
group.setPassphrase(in.readString());
group.setInterface(in.readString());
+ group.setNetworkId(in.readInt());
return group;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
new file mode 100644
index 0000000..3d8a476
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 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.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pGroupList;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
new file mode 100644
index 0000000..3459a5a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 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.
+ */
+package android.net.wifi.p2p;
+
+import java.util.Collection;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LruCache;
+
+
+/**
+ * A class representing a Wi-Fi P2p group list
+ *
+ * {@see WifiP2pManager}
+ * @hide
+ */
+public class WifiP2pGroupList implements Parcelable {
+
+ private static final int CREDENTIAL_MAX_NUM = 32;
+
+ private LruCache<Integer, WifiP2pGroup> mGroups;
+ private GroupDeleteListener mListener;
+ private boolean isClearCalled = false;
+
+ public interface GroupDeleteListener {
+ public void onDeleteGroup(int netId);
+ }
+
+ WifiP2pGroupList() {
+ this(null);
+ }
+
+ WifiP2pGroupList(GroupDeleteListener listener) {
+ mListener = listener;
+ mGroups = new LruCache<Integer, WifiP2pGroup>(CREDENTIAL_MAX_NUM) {
+ @Override
+ protected void entryRemoved(boolean evicted, Integer netId,
+ WifiP2pGroup oldValue, WifiP2pGroup newValue) {
+ if (mListener != null && !isClearCalled) {
+ mListener.onDeleteGroup(oldValue.getNetworkId());
+ }
+ }
+ };
+ }
+
+ /**
+ * Return the list of p2p group.
+ *
+ * @return the list of p2p group.
+ */
+ public Collection<WifiP2pGroup> getGroupList() {
+ return mGroups.snapshot().values();
+ }
+
+ /**
+ * Add the specified group to this group list.
+ *
+ * @param group
+ */
+ void add(WifiP2pGroup group) {
+ mGroups.put(group.getNetworkId(), group);
+ }
+
+ /**
+ * Remove the group with the specified network id from this group list.
+ *
+ * @param netId
+ */
+ void remove(int netId) {
+ mGroups.remove(netId);
+ }
+
+ /**
+ * Remove the group with the specified device address from this group list.
+ *
+ * @param deviceAddress
+ */
+ void remove(String deviceAddress) {
+ remove(getNetworkId(deviceAddress));
+ }
+
+ /**
+ * Clear the group.
+ */
+ boolean clear() {
+ if (mGroups.size() == 0) return false;
+ isClearCalled = true;
+ mGroups.evictAll();
+ isClearCalled = false;
+ return true;
+ }
+
+ /**
+ * Return the network id of the group owner profile with the specified p2p device
+ * address.
+ * If more than one persistent group of the same address is present in the list,
+ * return the first one.
+ *
+ * @param deviceAddress p2p device address.
+ * @return the network id. if not found, return -1.
+ */
+ int getNetworkId(String deviceAddress) {
+ if (deviceAddress == null) return -1;
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress)) {
+ // update cache ordered.
+ mGroups.get(grp.getNetworkId());
+ return grp.getNetworkId();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the network id of the group with the specified p2p device address
+ * and the ssid.
+ *
+ * @param deviceAddress p2p device address.
+ * @param ssid ssid.
+ * @return the network id. if not found, return -1.
+ */
+ int getNetworkId(String deviceAddress, String ssid) {
+ if (deviceAddress == null || ssid == null) {
+ return -1;
+ }
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress) &&
+ ssid.equals(grp.getNetworkName())) {
+ // update cache ordered.
+ mGroups.get(grp.getNetworkId());
+ return grp.getNetworkId();
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Return the group owner address of the group with the specified network id
+ *
+ * @param netId network id.
+ * @return the address. if not found, return null.
+ */
+ String getOwnerAddr(int netId) {
+ WifiP2pGroup grp = mGroups.get(netId);
+ if (grp != null) {
+ return grp.getOwner().deviceAddress;
+ }
+ return null;
+ }
+
+ /**
+ * Return true if this group list contains the specified network id.
+ * This function does NOT update LRU information.
+ * It means the internal queue is NOT reordered.
+ *
+ * @param netId network id.
+ * @return true if the specified network id is present in this group list.
+ */
+ boolean contains(int netId) {
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (netId == grp.getNetworkId()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ sbuf.append(grp).append("\n");
+ }
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ dest.writeInt(groups.size());
+ for(WifiP2pGroup group : groups) {
+ dest.writeParcelable(group, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<WifiP2pGroupList> CREATOR =
+ new Creator<WifiP2pGroupList>() {
+ public WifiP2pGroupList createFromParcel(Parcel in) {
+ WifiP2pGroupList grpList = new WifiP2pGroupList();
+
+ int deviceCount = in.readInt();
+ for (int i = 0; i < deviceCount; i++) {
+ grpList.add((WifiP2pGroup)in.readParcelable(null));
+ }
+ return grpList;
+ }
+
+ public WifiP2pGroupList[] newArray(int size) {
+ return new WifiP2pGroupList[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
index dce315a..8972b7e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
@@ -44,8 +44,8 @@
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("groupFormed: ").append(groupFormed)
- .append("isGroupOwner: ").append(isGroupOwner)
- .append("groupOwnerAddress: ").append(groupOwnerAddress);
+ .append(" isGroupOwner: ").append(isGroupOwner)
+ .append(" groupOwnerAddress: ").append(groupOwnerAddress);
return sbuf.toString();
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2c25e9d..6edc232 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -34,10 +34,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.WorkSource;
-import android.os.Messenger;
import android.util.Log;
import com.android.internal.util.AsyncChannel;
@@ -267,6 +267,13 @@
public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
/**
+ * Broadcast intent action indicating that remembered persistent groups have changed.
+ * @hide
+ */
+ public static final String WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION =
+ "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED";
+
+ /**
* The lookup key for a {@link #String} object.
* Retrieve with {@link android.os.Bundle#getString(String)}.
* @hide
@@ -436,6 +443,25 @@
/** @hide */
public static final int SHOW_PIN_REQUESTED = BASE + 58;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP = BASE + 59;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP_FAILED = BASE + 60;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP_SUCCEEDED = BASE + 61;
+
+ /** @hide */
+ public static final int REQUEST_PERSISTENT_GROUP_INFO = BASE + 62;
+ /** @hide */
+ public static final int RESPONSE_PERSISTENT_GROUP_INFO = BASE + 63;
+
+ /** @hide */
+ public static final int SET_WFD_INFO = BASE + 64;
+ /** @hide */
+ public static final int SET_WFD_INFO_FAILED = BASE + 65;
+ /** @hide */
+ public static final int SET_WFD_INFO_SUCCEEDED = BASE + 66;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -657,6 +683,15 @@
public void onDetached(int reason);
}
+ /** Interface for callback invocation when stored group info list is available {@hide}*/
+ public interface PersistentGroupInfoListener {
+ /**
+ * The requested stored p2p group info list is available
+ * @param groups Wi-Fi p2p group info list
+ */
+ public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups);
+ }
+
/**
* A channel that connects the application to the Wifi p2p framework.
* Most p2p operations require a Channel as an argument. An instance of Channel is obtained
@@ -713,6 +748,8 @@
case WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED:
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
case WifiP2pManager.SET_DEVICE_NAME_FAILED:
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP_FAILED:
+ case WifiP2pManager.SET_WFD_INFO_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
@@ -732,6 +769,8 @@
case WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED:
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
case WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED:
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED:
+ case WifiP2pManager.SET_WFD_INFO_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -786,6 +825,13 @@
mDialogListener = null;
}
break;
+ case WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO:
+ WifiP2pGroupList groups = (WifiP2pGroupList) message.obj;
+ if (listener != null) {
+ ((PersistentGroupInfoListener) listener).
+ onPersistentGroupInfoAvailable(groups);
+ }
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -995,7 +1041,8 @@
*/
public void createGroup(Channel c, ActionListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, c.putListener(listener));
+ c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
+ c.putListener(listener));
}
/**
@@ -1259,6 +1306,13 @@
c.mAsyncChannel.sendMessage(SET_DEVICE_NAME, 0, c.putListener(listener), d);
}
+ /** @hide */
+ public void setWFDInfo(
+ Channel c, WifiP2pWfdInfo wfdInfo,
+ ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(SET_WFD_INFO, 0, c.putListener(listener), wfdInfo);
+ }
/**
* Set dialog listener to over-ride system dialogs on p2p events. This function
@@ -1297,6 +1351,40 @@
}
/**
+ * Delete a stored persistent group from the system settings.
+ *
+ * <p> The function call immediately returns after sending a persistent group removal request
+ * to the framework. The application is notified of a success or failure to initiate
+ * group removal through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p>The persistent p2p group list stored in the system can be obtained by
+ * {@link #requestPersistentGroupInfo(Channel, PersistentGroupInfoListener)} and
+ * a network id can be obtained by {@link WifiP2pGroup#getNetworkId()}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param netId he network id of the p2p group.
+ * @param listener for callbacks on success or failure. Can be null.
+ * @hide
+ */
+ public void deletePersistentGroup(Channel c, int netId, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(DELETE_PERSISTENT_GROUP, netId, c.putListener(listener));
+ }
+
+ /**
+ * Request a list of all the persistent p2p groups stored in system.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callback when persistent group info list is available. Can be null.
+ * @hide
+ */
+ public void requestPersistentGroupInfo(Channel c, PersistentGroupInfoListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener));
+ }
+
+ /**
* Get a reference to WifiP2pService handler. This is used to establish
* an AsyncChannel communication with WifiService
*
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 6978084..a6770bd 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -45,6 +45,7 @@
import android.net.wifi.WifiNative;
import android.net.wifi.WifiStateMachine;
import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener;
import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
@@ -61,6 +62,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Parcelable.Creator;
import android.provider.Settings;
import android.text.TextUtils;
@@ -118,6 +120,13 @@
private static final Boolean JOIN_GROUP = true;
private static final Boolean FORM_GROUP = false;
+ private static final Boolean TRY_REINVOCATION = true;;
+ private static final Boolean NO_REINVOCATION = false;
+
+ private static final int CONNECT_FAILURE = -1;
+ private static final int CONNECT_SUCCESS = 0;
+ private static final int NEEDS_PROVISION_REQ = 1;
+
/* Two minutes comes from the wpa_supplicant setting */
private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
private static int mGroupCreatingTimeoutIndex = 0;
@@ -191,6 +200,84 @@
private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
private static final String SERVER_ADDRESS = "192.168.49.1";
+ /**
+ * Error code definition.
+ * see the Table.8 in the WiFi Direct specification for the detail.
+ */
+ public static enum P2pStatus {
+ /* Success. */
+ SUCCESS,
+
+ /* The target device is currently unavailable. */
+ INFORMATION_IS_CURRENTLY_UNAVAILABLE,
+
+ /* Protocol error. */
+ INCOMPATIBLE_PARAMETERS,
+
+ /* The target device reached the limit of the number of the connectable device.
+ * For example, device limit or group limit is set. */
+ LIMIT_REACHED,
+
+ /* Protocol error. */
+ INVALID_PARAMETER,
+
+ /* Unable to accommodate request. */
+ UNABLE_TO_ACCOMMODATE_REQUEST,
+
+ /* Previous protocol error, or disruptive behavior. */
+ PREVIOUS_PROTOCOL_ERROR,
+
+ /* There is no common channels the both devices can use. */
+ NO_COMMON_CHANNE,
+
+ /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
+ * but device B has removed the specified credential already. */
+ UNKNOWN_P2P_GROUP,
+
+ /* Both p2p devices indicated an intent of 15 in group owner negotiation. */
+ BOTH_GO_INTENT_15,
+
+ /* Incompatible provisioning method. */
+ INCOMPATIBLE_PROVISIONING_METHOD,
+
+ /* Rejected by user */
+ REJECTED_BY_USER,
+
+ /* Unknown error */
+ UNKNOWN;
+
+ public static P2pStatus valueOf(int error) {
+ switch(error) {
+ case 0 :
+ return SUCCESS;
+ case 1:
+ return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
+ case 2:
+ return INCOMPATIBLE_PARAMETERS;
+ case 3:
+ return LIMIT_REACHED;
+ case 4:
+ return INVALID_PARAMETER;
+ case 5:
+ return UNABLE_TO_ACCOMMODATE_REQUEST;
+ case 6:
+ return PREVIOUS_PROTOCOL_ERROR;
+ case 7:
+ return NO_COMMON_CHANNE;
+ case 8:
+ return UNKNOWN_P2P_GROUP;
+ case 9:
+ return BOTH_GO_INTENT_15;
+ case 10:
+ return INCOMPATIBLE_PROVISIONING_METHOD;
+ case 11:
+ return REJECTED_BY_USER;
+ default:
+ return UNKNOWN;
+ }
+ }
+ }
+
public WifiP2pService(Context context) {
mContext = context;
@@ -273,6 +360,16 @@
private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+ private WifiP2pGroupList mGroups = new WifiP2pGroupList(
+ new GroupDeleteListener() {
+ @Override
+ public void onDeleteGroup(int netId) {
+ if (DBG) logd("called onDeleteGroup() netId=" + netId);
+ mWifiNative.removeNetwork(netId);
+ mWifiNative.saveConfig();
+ sendP2pPersistentGroupsChangedBroadcast();
+ }
+ });
private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
private WifiP2pGroup mGroup;
@@ -395,6 +492,14 @@
replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
WifiP2pManager.BUSY);
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.SET_WFD_INFO:
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.BUSY);
+ break;
case WifiP2pManager.REQUEST_PEERS:
replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
break;
@@ -404,6 +509,10 @@
case WifiP2pManager.REQUEST_GROUP_INFO:
replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
break;
+ case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
+ replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
+ mGroups);
+ break;
case WifiP2pManager.SET_DIALOG_LISTENER:
String appPkgName = (String)message.getData().getString(
WifiP2pManager.APP_PKG_BUNDLE_KEY);
@@ -520,6 +629,14 @@
replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.SET_WFD_INFO:
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
default:
return NOT_HANDLED;
}
@@ -626,10 +743,13 @@
break;
case WifiStateMachine.CMD_DISABLE_P2P:
if (mPeers.clear()) sendP2pPeersChangedBroadcast();
+ if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
+
mWifiNative.closeSupplicantConnection();
transitionTo(mP2pDisablingState);
break;
case WifiP2pManager.SET_DEVICE_NAME:
+ {
WifiP2pDevice d = (WifiP2pDevice) message.obj;
if (d != null && setAndPersistDeviceName(d.deviceName)) {
if (DBG) logd("set device name " + d.deviceName);
@@ -639,6 +759,18 @@
WifiP2pManager.ERROR);
}
break;
+ }
+ case WifiP2pManager.SET_WFD_INFO:
+ {
+ WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
+ if (d != null && setWfdInfo(d)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ }
case WifiP2pManager.DISCOVER_PEERS:
// do not send service discovery request while normal find operation.
clearSupplicantServiceRequest();
@@ -734,6 +866,11 @@
sendServiceResponse(resp);
}
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ if (DBG) logd(getName() + " delete persistent group");
+ mGroups.remove(message.arg1);
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
+ break;
default:
return NOT_HANDLED;
}
@@ -768,47 +905,35 @@
/* Update group capability before connect */
int gc = mWifiNative.getGroupCapability(config.deviceAddress);
mPeers.updateGroupCapability(config.deviceAddress, gc);
-
- if (mSavedPeerConfig != null && config.deviceAddress.equals(
- mSavedPeerConfig.deviceAddress)) {
- mSavedPeerConfig = config;
-
- //Stop discovery before issuing connect
- mWifiNative.p2pStopFind();
- if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- } else {
- p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
- }
- transitionTo(mGroupNegotiationState);
- } else {
- mSavedPeerConfig = config;
- int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
- if (netId >= 0) {
- //TODO: if failure, remove config and do a regular p2pConnect()
- mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
- } else {
- //Stop discovery before issuing connect
- mWifiNative.p2pStopFind();
- //If peer is a GO, we do not need to send provisional discovery,
- //the supplicant takes care of it.
- if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
- if (DBG) logd("Sending join to GO");
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- transitionTo(mGroupNegotiationState);
- } else {
- if (DBG) logd("Sending prov disc");
- transitionTo(mProvisionDiscoveryState);
- }
- }
+ int connectRet = connect(config, TRY_REINVOCATION);
+ if (connectRet == CONNECT_FAILURE) {
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+ break;
}
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+ if (connectRet == NEEDS_PROVISION_REQ) {
+ if (DBG) logd("Sending prov disc");
+ transitionTo(mProvisionDiscoveryState);
+ break;
+ }
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ if (mWifiNative.p2pStopFind()) {
+ // When discovery stops in inactive state, flush to clear
+ // state peer data
+ mWifiNative.p2pFlush();
+ mServiceDiscReqId = null;
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.ERROR);
+ }
break;
case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
mSavedPeerConfig = (WifiP2pConfig) message.obj;
-
mAutonomousGroup = false;
mJoinExistingGroup = false;
if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
@@ -848,13 +973,6 @@
transitionTo(mUserAuthorizingInvitationState);
}
break;
- case WifiMonitor.P2P_FIND_STOPPED_EVENT:
- // When discovery stops in inactive state, flush to clear
- // state peer data
- mWifiNative.p2pFlush();
- mServiceDiscReqId = null;
- sendP2pDiscoveryChangedBroadcast(false);
- break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -865,16 +983,44 @@
break;
case WifiP2pManager.CREATE_GROUP:
mAutonomousGroup = true;
- if (mWifiNative.p2pGroupAdd()) {
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+ int netId = message.arg1;
+ boolean ret = false;
+ if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // check if the go persistent group is present.
+ netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
+ if (netId != -1) {
+ ret = mWifiNative.p2pGroupAdd(netId);
+ } else {
+ ret = mWifiNative.p2pGroupAdd(true);
+ }
+ } else {
+ ret = mWifiNative.p2pGroupAdd(false);
+ }
+
+ if (ret) {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+ WifiP2pManager.ERROR);
+ // remain at this state.
+ }
+ break;
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) logd(getName() + " group started");
+
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // This is an invocation case.
+ mAutonomousGroup = false;
+ deferMessage(message);
+ transitionTo(mGroupNegotiationState);
} else {
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
- WifiP2pManager.ERROR);
+ return NOT_HANDLED;
}
- transitionTo(mGroupNegotiationState);
break;
default:
- return NOT_HANDLED;
+ return NOT_HANDLED;
}
return HANDLED;
}
@@ -941,10 +1087,7 @@
@Override
public void enter() {
if (DBG) logd(getName());
- if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
- mSavedPeerConfig)) {
- notifyInvitationReceived();
- }
+ notifyInvitationReceived();
}
@Override
@@ -953,11 +1096,10 @@
boolean ret = HANDLED;
switch (message.what) {
case PEER_CONNECTION_USER_ACCEPT:
- //TODO: handle persistence
- if (mJoinExistingGroup) {
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- } else {
- p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
+ if (connect(mSavedPeerConfig, TRY_REINVOCATION) == CONNECT_FAILURE) {
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
}
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
@@ -1000,7 +1142,7 @@
if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
}
break;
@@ -1013,11 +1155,14 @@
if (DBG) logd("Found a match " + mSavedPeerConfig);
/* we already have the pin */
if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
} else {
mJoinExistingGroup = false;
- transitionTo(mUserAuthorizingInvitationState);
+ if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
+ mSavedPeerConfig)) {
+ transitionTo(mUserAuthorizingInvitationState);
+ }
}
}
break;
@@ -1029,7 +1174,7 @@
if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
mSavedPeerConfig.wps.pin = provDisc.pin;
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
if (!sendShowPinReqToFrontApp(provDisc.pin)) {
notifyInvitationSent(provDisc.pin, device.deviceAddress);
}
@@ -1062,6 +1207,17 @@
case WifiMonitor.P2P_GROUP_STARTED_EVENT:
mGroup = (WifiP2pGroup) message.obj;
if (DBG) logd(getName() + " group started");
+
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ /*
+ * update cache information and set network id to mGroup.
+ */
+ updatePersistentNetworks();
+ String devAddr = mGroup.getOwner().deviceAddress;
+ mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
+ mGroup.getNetworkName()));
+ }
+
if (mGroup.isGroupOwner()) {
startDhcpServer(mGroup.getInterface());
} else {
@@ -1090,6 +1246,29 @@
// failure causes supplicant issues. Ignore right now.
case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
break;
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ P2pStatus status = (P2pStatus)message.obj;
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ // wait P2P_GROUP_STARTED_EVENT.
+ break;
+ } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mSavedPeerConfig.netId;
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
+ }
+ }
+
+ // invocation is failed or deferred. Try another way to connect.
+ mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ if (connect(mSavedPeerConfig, NO_REINVOCATION) == CONNECT_FAILURE) {
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -1152,7 +1331,7 @@
}
}
sendP2pPeersChangedBroadcast();
- if (DBG) loge(getName() + " ap sta disconnected");
+ if (DBG) logd(getName() + " ap sta disconnected");
} else {
loge("Disconnect on unknown device: " + device);
}
@@ -1172,7 +1351,7 @@
}
break;
case WifiP2pManager.REMOVE_GROUP:
- if (DBG) loge(getName() + " remove group");
+ if (DBG) logd(getName() + " remove group");
if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
} else {
@@ -1181,7 +1360,7 @@
}
break;
case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- if (DBG) loge(getName() + " group removed");
+ if (DBG) logd(getName() + " group removed");
Collection <WifiP2pDevice> devices = mGroup.getClientList();
boolean changed = false;
for (WifiP2pDevice d : mPeers.getDeviceList()) {
@@ -1252,6 +1431,7 @@
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
} else {
logd("Inviting device : " + config.deviceAddress);
+ mSavedPeerConfig = config;
if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
@@ -1263,6 +1443,29 @@
}
// TODO: figure out updating the status to declined when invitation is rejected
break;
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ P2pStatus status = (P2pStatus)message.obj;
+ logd("===> INVITATION RESULT EVENT : " + status);
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ break;
+ } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mGroup.getNetworkId();
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ if (!removeClientFromList(netId,
+ mSavedPeerConfig.deviceAddress, false)) {
+ // not found the client on the list
+ Slog.e(TAG, "Already removed the client, ignore");
+ break;
+ }
+ // try invitation.
+ sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+ }
+ }
+ break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -1294,7 +1497,7 @@
public void exit() {
mSavedProvDiscDevice = null;
updateThisDevice(WifiP2pDevice.AVAILABLE);
- setWifiP2pInfoOnGroupTermination();
+ resetWifiP2pInfo();
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
sendP2pConnectionChangedBroadcast();
}
@@ -1304,7 +1507,6 @@
@Override
public void enter() {
if (DBG) logd(getName());
-
notifyInvitationReceived();
}
@@ -1354,7 +1556,7 @@
intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_DISABLED);
}
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendP2pDiscoveryChangedBroadcast(boolean started) {
@@ -1368,20 +1570,20 @@
intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendThisDeviceChangedBroadcast() {
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendP2pPeersChangedBroadcast() {
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendP2pConnectionChangedBroadcast() {
@@ -1391,7 +1593,14 @@
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
- mContext.sendStickyBroadcast(intent);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendP2pPersistentGroupsChangedBroadcast() {
+ if (DBG) logd("sending p2p persistent groups changed broadcast");
+ Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
private void startDhcpServer(String intf) {
@@ -1521,18 +1730,253 @@
dialog.show();
}
- //TODO: implement when wpa_supplicant is fixed
- private int configuredNetworkId(String deviceAddress) {
+ /**
+ * Synchronize the persistent group list between
+ * wpa_supplicant and mGroups.
+ */
+ private void updatePersistentNetworks() {
+ String listStr = mWifiNative.listNetworks();
+
+ boolean isSaveRequired = false;
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ if (result == null || result.length < 4) {
+ continue;
+ }
+ // network-id | ssid | bssid | flags
+ int netId = -1;
+ String ssid = result[1];
+ String bssid = result[2];
+ String flags = result[3];
+ try {
+ netId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ e.printStackTrace();
+ continue;
+ }
+
+ if (flags.indexOf("[CURRENT]") != -1) {
+ continue;
+ }
+ if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
+ /*
+ * The unused profile is sometimes remained when the p2p group formation is failed.
+ * So, we clean up the p2p group here.
+ */
+ if (DBG) logd("clean up the unused persistent group. netId=" + netId);
+ mWifiNative.removeNetwork(netId);
+ isSaveRequired = true;
+ continue;
+ }
+
+ if (mGroups.contains(netId)) {
+ continue;
+ }
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setNetworkId(netId);
+ group.setNetworkName(ssid);
+ String mode = mWifiNative.getNetworkVariable(netId, "mode");
+ if (mode != null && mode.equals("3")) {
+ group.setIsGroupOwner(true);
+ }
+ if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
+ group.setOwner(mThisDevice);
+ } else {
+ WifiP2pDevice device = new WifiP2pDevice();
+ device.deviceAddress = bssid;
+ group.setOwner(device);
+ }
+ mGroups.add(group);
+ isSaveRequired = true;
+ }
+
+ if (isSaveRequired) {
+ sendP2pPersistentGroupsChangedBroadcast();
+ mWifiNative.saveConfig();
+ }
+ }
+
+ /**
+ * Try to connect to the target device.
+ *
+ * Use the persistent credential if it has been stored.
+ *
+ * @param config
+ * @param tryInvocation if true, try to invoke. Otherwise, never try to invoke.
+ * @return
+ */
+ private int connect(WifiP2pConfig config, boolean tryInvocation) {
+
+ if (config == null) {
+ loge("invalid argument.");
+ return CONNECT_FAILURE;
+ }
+
+ boolean isResp = (mSavedPeerConfig != null &&
+ config.deviceAddress.equals(mSavedPeerConfig.deviceAddress));
+ mSavedPeerConfig = config;
+
+ WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+ if (dev == null) {
+ loge("target device is not found.");
+ return CONNECT_FAILURE;
+ }
+
+ boolean join = dev.isGroupOwner();
+ String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
+ if (DBG) logd("target ssid is " + ssid + " join:" + join);
+
+ if (join && dev.isGroupLimit()) {
+ if (DBG) logd("target device reaches group limit.");
+
+ // if the target group has reached the limit,
+ // try group formation.
+ join = false;
+ } else if (join) {
+ int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
+ if (netId >= 0) {
+ // Skip WPS and start 4way handshake immediately.
+ if (!mWifiNative.p2pGroupAdd(netId)) {
+ return CONNECT_FAILURE;
+ }
+ return CONNECT_SUCCESS;
+ }
+ }
+
+ if (!join && dev.isDeviceLimit()) {
+ loge("target device reaches the device limit.");
+ return CONNECT_FAILURE;
+ }
+
+ if (!join && tryInvocation && dev.isInvitationCapable()) {
+ int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ if (config.netId >= 0) {
+ if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
+ netId = config.netId;
+ }
+ } else {
+ netId = mGroups.getNetworkId(dev.deviceAddress);
+ }
+ if (netId < 0) {
+ netId = getNetworkIdFromClientList(dev.deviceAddress);
+ }
+ if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
+ if (netId >= 0) {
+
+ // Invoke the persistent group.
+ if (!mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
+ loge("p2pReinvoke() failed");
+ return CONNECT_FAILURE;
+ }
+ // Save network id. It'll be used when an invitation result event is received.
+ mSavedPeerConfig.netId = netId;
+ return CONNECT_SUCCESS;
+ }
+ }
+
+ //Stop discovery before issuing connect
+ mWifiNative.p2pStopFind();
+
+ if (!isResp) {
+ return NEEDS_PROVISION_REQ;
+ }
+
+ p2pConnectWithPinDisplay(config);
+ return CONNECT_SUCCESS;
+ }
+
+ /**
+ * Return the network id of the group owner profile which has the p2p client with
+ * the specified device address in it's client list.
+ * If more than one persistent group of the same address is present in its client
+ * lists, return the first one.
+ *
+ * @param deviceAddress p2p device address.
+ * @return the network id. if not found, return -1.
+ */
+ private int getNetworkIdFromClientList(String deviceAddress) {
+ if (deviceAddress == null) return -1;
+
+ Collection<WifiP2pGroup> groups = mGroups.getGroupList();
+ for (WifiP2pGroup group : groups) {
+ int netId = group.getNetworkId();
+ String[] p2pClientList = getClientList(netId);
+ if (p2pClientList == null) continue;
+ for (String client : p2pClientList) {
+ if (deviceAddress.equalsIgnoreCase(client)) {
+ return netId;
+ }
+ }
+ }
return -1;
}
+ /**
+ * Return p2p client list associated with the specified network id.
+ * @param netId network id.
+ * @return p2p client list. if not found, return null.
+ */
+ private String[] getClientList(int netId) {
+ String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
+ if (p2pClients == null) {
+ return null;
+ }
+ return p2pClients.split(" ");
+ }
+
+ /**
+ * Remove the specified p2p client from the specified profile.
+ * @param netId network id of the profile.
+ * @param addr p2p client address to be removed.
+ * @param isRemovable if true, remove the specified profile if its client list becomes empty.
+ * @return whether removing the specified p2p client is successful or not.
+ */
+ private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
+ StringBuilder modifiedClientList = new StringBuilder();
+ String[] currentClientList = getClientList(netId);
+ boolean isClientRemoved = false;
+ if (currentClientList != null) {
+ for (String client : currentClientList) {
+ if (!client.equalsIgnoreCase(addr)) {
+ modifiedClientList.append(" ");
+ modifiedClientList.append(client);
+ } else {
+ isClientRemoved = true;
+ }
+ }
+ }
+ if (modifiedClientList.length() == 0 && isRemovable) {
+ // the client list is empty. so remove it.
+ if (DBG) logd("Remove unknown network");
+ mGroups.remove(netId);
+ return true;
+ }
+
+ if (!isClientRemoved) {
+ // specified p2p client is not found. already removed.
+ return false;
+ }
+
+ if (DBG) logd("Modified client list: " + modifiedClientList);
+ if (modifiedClientList.length() == 0) {
+ modifiedClientList.append("\"\"");
+ }
+ mWifiNative.setNetworkVariable(netId,
+ "p2p_client_list", modifiedClientList.toString());
+ mWifiNative.saveConfig();
+ return true;
+ }
+
private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
mWifiP2pInfo.groupFormed = true;
mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
}
- private void setWifiP2pInfoOnGroupTermination() {
+ private void resetWifiP2pInfo() {
mWifiP2pInfo.groupFormed = false;
mWifiP2pInfo.isGroupOwner = false;
mWifiP2pInfo.groupOwnerAddress = null;
@@ -1547,8 +1991,14 @@
return deviceAddress;
}
- private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
- String pin = mWifiNative.p2pConnect(config, join);
+ private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
+ WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+ if (dev == null) {
+ loge("target device is not found " + config.deviceAddress);
+ return;
+ }
+
+ String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
try {
Integer.parseInt(pin);
if (!sendShowPinReqToFrontApp(pin)) {
@@ -1589,6 +2039,27 @@
return true;
}
+ private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
+ boolean success;
+
+ if (!wfdInfo.isWfdEnabled()) {
+ success = mWifiNative.setWfdEnable(false);
+ } else {
+ success =
+ mWifiNative.setWfdEnable(true)
+ && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
+ }
+
+ if (!success) {
+ loge("Failed to set wfd properties");
+ return false;
+ }
+
+ mThisDevice.wfdInfo = wfdInfo;
+ sendThisDeviceChangedBroadcast();
+ return true;
+ }
+
private void initializeP2pSettings() {
mWifiNative.setPersistentReconnect(true);
mThisDevice.deviceName = getPersistedDeviceName();
@@ -1611,6 +2082,8 @@
mWifiNative.p2pServiceFlush();
mServiceTransactionId = 0;
mServiceDiscReqId = null;
+
+ updatePersistentNetworks();
}
private void updateThisDevice(int status) {
@@ -1619,6 +2092,9 @@
}
private void handleGroupCreationFailure() {
+ resetWifiP2pInfo();
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
+ sendP2pConnectionChangedBroadcast();
mSavedPeerConfig = null;
/* After cancelling group formation, new connections on existing peers can fail
* at supplicant. Flush and restart discovery */
@@ -1739,7 +2215,7 @@
//Application does not have transaction id information
//go through stored requests to remove
boolean removed = false;
- for (int i=0; i < clientInfo.mReqList.size(); i++) {
+ for (int i=0; i<clientInfo.mReqList.size(); i++) {
if (req.equals(clientInfo.mReqList.valueAt(i))) {
removed = true;
clientInfo.mReqList.removeAt(i);
@@ -2078,5 +2554,4 @@
mServList = new ArrayList<WifiP2pServiceInfo>();
}
}
-
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
new file mode 100644
index 0000000..9dd3e4a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A class representing Wifi Display information for a device
+ * @hide
+ */
+public class WifiP2pWfdInfo implements Parcelable {
+
+ private static final String TAG = "WifiP2pWfdInfo";
+
+ private boolean mWfdEnabled;
+
+ private int mDeviceInfo;
+
+ public static final int WFD_SOURCE = 0;
+ public static final int PRIMARY_SINK = 1;
+ public static final int SECONDARY_SINK = 2;
+ public static final int SOURCE_OR_PRIMARY_SINK = 3;
+
+ /* Device information bitmap */
+ /** One of {@link #WFD_SOURCE}, {@link #PRIMARY_SINK}, {@link #SECONDARY_SINK}
+ * or {@link #SOURCE_OR_PRIMARY_SINK}
+ */
+ private static final int DEVICE_TYPE = 0x3;
+ private static final int COUPLED_SINK_SUPPORT_AT_SOURCE = 0x4;
+ private static final int COUPLED_SINK_SUPPORT_AT_SINK = 0x8;
+ private static final int SESSION_AVAILABLE = 0x30;
+ private static final int SESSION_AVAILABLE_BIT1 = 0x10;
+ private static final int SESSION_AVAILABLE_BIT2 = 0x20;
+
+ private int mCtrlPort;
+
+ private int mMaxThroughput;
+
+ public WifiP2pWfdInfo() {
+ }
+
+ public boolean isWfdEnabled() {
+ return mWfdEnabled;
+ }
+
+ public void setWfdEnabled(boolean enabled) {
+ mWfdEnabled = enabled;
+ }
+
+ public int getDeviceType() {
+ return (mDeviceInfo & DEVICE_TYPE);
+ }
+
+ public boolean setDeviceType(int deviceType) {
+ if (deviceType >= WFD_SOURCE && deviceType <= SOURCE_OR_PRIMARY_SINK) {
+ mDeviceInfo |= deviceType;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isCoupledSinkSupportedAtSource() {
+ return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0;
+ }
+
+ public void setCoupledSinkSupportAtSource(boolean enabled) {
+ if (enabled ) {
+ mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK;
+ } else {
+ mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK;
+ }
+ }
+
+ public boolean isCoupledSinkSupportedAtSink() {
+ return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0;
+ }
+
+ public void setCoupledSinkSupportAtSink(boolean enabled) {
+ if (enabled ) {
+ mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK;
+ } else {
+ mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK;
+ }
+ }
+
+ public boolean isSessionAvailable() {
+ return (mDeviceInfo & SESSION_AVAILABLE) != 0;
+ }
+
+ public void setSessionAvailable(boolean enabled) {
+ if (enabled) {
+ mDeviceInfo |= SESSION_AVAILABLE_BIT1;
+ mDeviceInfo &= ~SESSION_AVAILABLE_BIT2;
+ } else {
+ mDeviceInfo &= ~SESSION_AVAILABLE;
+ }
+ }
+
+ public int getControlPort() {
+ return mCtrlPort;
+ }
+
+ public void setControlPort(int port) {
+ mCtrlPort = port;
+ }
+
+ public void setMaxThroughput(int maxThroughput) {
+ mMaxThroughput = maxThroughput;
+ }
+
+ public int getMaxThroughput() {
+ return mMaxThroughput;
+ }
+
+ public String getDeviceInfoHex() {
+ return String.format("%04x%04x%04x%04x", 6, mDeviceInfo, mCtrlPort, mMaxThroughput);
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("WFD enabled: ").append(mWfdEnabled);
+ sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo);
+ sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort);
+ sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput);
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor */
+ public WifiP2pWfdInfo(WifiP2pWfdInfo source) {
+ if (source != null) {
+ mDeviceInfo = source.mDeviceInfo;
+ mCtrlPort = source.mCtrlPort;
+ mMaxThroughput = source.mMaxThroughput;
+ }
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWfdEnabled ? 1 : 0);
+ dest.writeInt(mDeviceInfo);
+ dest.writeInt(mCtrlPort);
+ dest.writeInt(mMaxThroughput);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mWfdEnabled = (in.readInt() == 1);
+ mDeviceInfo = in.readInt();
+ mCtrlPort = in.readInt();
+ mMaxThroughput = in.readInt();
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<WifiP2pWfdInfo> CREATOR =
+ new Creator<WifiP2pWfdInfo>() {
+ public WifiP2pWfdInfo createFromParcel(Parcel in) {
+ WifiP2pWfdInfo device = new WifiP2pWfdInfo();
+ device.readFromParcel(in);
+ return device;
+ }
+
+ public WifiP2pWfdInfo[] newArray(int size) {
+ return new WifiP2pWfdInfo[size];
+ }
+ };
+}